Structure the tech specs directory (#836)

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

View file

@ -0,0 +1,135 @@
---
layout: default
title: "Especificação Técnica de Carregamento de Conhecimento via Linha de Comando"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Carregamento de Conhecimento via Linha de Comando
> **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.
## Visão Geral
Esta especificação descreve as interfaces de linha de comando para carregar conhecimento no TrustGraph, permitindo que os usuários ingiram dados de várias fontes por meio de ferramentas de linha de comando. A integração suporta quatro casos de uso primários:
1. **[Caso de Uso 1]**: [Descrição]
2. **[Caso de Uso 2]**: [Descrição]
3. **[Caso de Uso 3]**: [Descrição]
4. **[Caso de Uso 4]**: [Descrição]
## Objetivos
- **[Objetivo 1]**: [Descrição]
- **[Objetivo 2]**: [Descrição]
- **[Objetivo 3]**: [Descrição]
- **[Objetivo 4]**: [Descrição]
- **[Objetivo 5]**: [Descrição]
- **[Objetivo 6]**: [Descrição]
- **[Objetivo 7]**: [Descrição]
- **[Objetivo 8]**: [Descrição]
## Contexto
[Descreva o estado atual e as limitações que esta especificação aborda]
As limitações atuais incluem:
- [Limitação 1]
- [Limitação 2]
- [Limitação 3]
- [Limitação 4]
Esta especificação aborda essas lacunas ao [descrição]. Ao [capacidade], o TrustGraph pode:
- [Benefício 1]
- [Benefício 2]
- [Benefício 3]
- [Benefício 4]
## Design Técnico
### Arquitetura
O carregamento de conhecimento via linha de comando requer os seguintes componentes técnicos:
1. **[Componente 1]**
- [Descrição da funcionalidade do componente]
- [Características principais]
- [Pontos de integração]
Módulo: [caminho-do-módulo]
2. **[Componente 2]**
- [Descrição da funcionalidade do componente]
- [Características principais]
- [Pontos de integração]
Módulo: [caminho-do-módulo]
3. **[Componente 3]**
- [Descrição da funcionalidade do componente]
- [Características principais]
- [Pontos de integração]
Módulo: [caminho-do-módulo]
### Modelos de Dados
#### [Modelo de Dados 1]
[Descrição do modelo de dados e estrutura]
Exemplo:
```
[Example data structure]
```
Esta abordagem permite:
- [Benefício 1]
- [Benefício 2]
- [Benefício 3]
- [Benefício 4]
### APIs
Novas APIs:
- [Descrição da API 1]
- [Descrição da API 2]
- [Descrição da API 3]
APIs modificadas:
- [API modificada 1] - [Descrição das alterações]
- [API modificada 2] - [Descrição das alterações]
### Detalhes de implementação
[Abordagem e convenções de implementação]
[Notas adicionais de implementação]
## Considerações de segurança
[Considerações de segurança específicas para esta implementação]
## Considerações de desempenho
[Considerações de desempenho e possíveis gargalos]
## Estratégia de testes
[Abordagem e estratégia de testes]
## Plano de migração
[Estratégia de migração, se aplicável]
## Cronograma
[Informações sobre o cronograma, se especificadas]
## Perguntas pendentes
- [Pergunta pendente 1]
- [Pergunta pendente 2]
## Referências
[Referências, se aplicável]

View file

@ -0,0 +1,280 @@
---
layout: default
title: "Explicabilidade do Agente: Registro de Proveniência"
parent: "Portuguese (Beta)"
---
# Explicabilidade do Agente: Registro de Proveniência
> **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.
## Visão Geral
Adicionar o registro de proveniência ao loop do agente React para que as sessões do agente possam ser rastreadas e depuradas usando a mesma infraestrutura de explicabilidade do GraphRAG.
**Decisões de Design:**
- Escrever em `urn:graph:retrieval` (grafo de explicabilidade genérico)
- Cadeia de dependência linear por enquanto (análise N → foiDerivadoDe → análise N-1)
- As ferramentas são caixas pretas (registrar apenas entrada/saída)
- Suporte a DAG (Directed Acyclic Graph - Grafo Acíclico Direcionado) adiado para uma iteração futura
## Tipos de Entidade
Tanto o GraphRAG quanto o Agent usam PROV-O como a ontologia base, com subtipos específicos do TrustGraph:
### Tipos do GraphRAG
| Entidade | Tipo PROV-O | Tipos TG | Descrição |
|--------|-------------|----------|-------------|
| Pergunta | `prov:Activity` | `tg:Question`, `tg:GraphRagQuestion` | A consulta do usuário |
| Exploração | `prov:Entity` | `tg:Exploration` | Arestas recuperadas do grafo de conhecimento |
| Foco | `prov:Entity` | `tg:Focus` | Arestas selecionadas com raciocínio |
| Síntese | `prov:Entity` | `tg:Synthesis` | Resposta final |
### Tipos do Agente
| Entidade | Tipo PROV-O | Tipos TG | Descrição |
|--------|-------------|----------|-------------|
| Pergunta | `prov:Activity` | `tg:Question`, `tg:AgentQuestion` | A consulta do usuário |
| Análise | `prov:Entity` | `tg:Analysis` | Cada ciclo de pensar/agir/observar |
| Conclusão | `prov:Entity` | `tg:Conclusion` | Resposta final |
### Tipos do Document RAG
| Entidade | Tipo PROV-O | Tipos TG | Descrição |
|--------|-------------|----------|-------------|
| Pergunta | `prov:Activity` | `tg:Question`, `tg:DocRagQuestion` | A consulta do usuário |
| Exploração | `prov:Entity` | `tg:Exploration` | Trechos recuperados do armazenamento de documentos |
| Síntese | `prov:Entity` | `tg:Synthesis` | Resposta final |
**Observação:** O Document RAG usa um subconjunto dos tipos do GraphRAG (sem a etapa de Foco, pois não há seleção/raciocínio de arestas).
### Subtipos de Pergunta
Todas as entidades de Pergunta compartilham `tg:Question` como um tipo base, mas têm um subtipo específico para identificar o mecanismo de recuperação:
| Subtipo | Padrão URI | Mecanismo |
|---------|-------------|-----------|
| `tg:GraphRagQuestion` | `urn:trustgraph:question:{uuid}` | RAG de grafo de conhecimento |
| `tg:DocRagQuestion` | `urn:trustgraph:docrag:{uuid}` | RAG de documento/trecho |
| `tg:AgentQuestion` | `urn:trustgraph:agent:{uuid}` | Agente ReAct |
Isso permite consultar todas as perguntas via `tg:Question`, filtrando por mecanismo específico através do subtipo.
## Modelo de Proveniência
```
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 Proveniência de Documentos 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
```
## Alterações Necessárias
### 1. Alterações no Esquema
**Arquivo:** `trustgraph-base/trustgraph/schema/services/agent.py`
Adicionar os campos `session_id` e `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
```
**Arquivo:** `trustgraph-base/trustgraph/messaging/translators/agent.py`
Atualizar o tradutor para lidar com `session_id` e `collection` tanto em `to_pulsar()` quanto em `from_pulsar()`.
### 2. Adicionar Produtor de Explicabilidade ao Serviço de Agente
**Arquivo:** `trustgraph-flow/trustgraph/agent/react/service.py`
Registrar um "produtor de explicabilidade" (mesmo padrão do GraphRAG):
```python
from ... base import ProducerSpec
from ... schema import Triples
# In __init__:
self.register_specification(
ProducerSpec(
name = "explainability",
schema = Triples,
)
)
```
### 3. Geração de Triplas de Proveniência
**Arquivo:** `trustgraph-base/trustgraph/provenance/agent.py`
Crie funções auxiliares (semelhantes a `question_triples`, `exploration_triples`, etc. do 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. Definições de Tipo
**Arquivo:** `trustgraph-base/trustgraph/provenance/namespaces.py`
Adicionar tipos de entidade de explicabilidade e 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"
```
## Arquivos Modificados
| Arquivo | Alteração |
|------|--------|
| `trustgraph-base/trustgraph/schema/services/agent.py` | Adiciona session_id e collection a AgentRequest |
| `trustgraph-base/trustgraph/messaging/translators/agent.py` | Atualiza o tradutor para novos campos |
| `trustgraph-base/trustgraph/provenance/namespaces.py` | Adiciona tipos de entidade, predicados de agente e predicados Document RAG |
| `trustgraph-base/trustgraph/provenance/triples.py` | Adiciona tipos TG aos construtores de triplas GraphRAG, adiciona construtores de triplas Document RAG |
| `trustgraph-base/trustgraph/provenance/uris.py` | Adiciona geradores de URI Document RAG |
| `trustgraph-base/trustgraph/provenance/__init__.py` | Exporta novos tipos, predicados e funções Document RAG |
| `trustgraph-base/trustgraph/schema/services/retrieval.py` | Adiciona explain_id e explain_graph a DocumentRagResponse |
| `trustgraph-base/trustgraph/messaging/translators/retrieval.py` | Atualiza DocumentRagResponseTranslator para campos de explicabilidade |
| `trustgraph-flow/trustgraph/agent/react/service.py` | Adiciona lógica de produção e gravação de explicabilidade |
| `trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py` | Adiciona callback de explicabilidade e emite triplas de procedência |
| `trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` | Adiciona produtor de explicabilidade e conecta o callback |
| `trustgraph-cli/trustgraph/cli/show_explain_trace.py` | Lida com tipos de rastreamento de agente |
| `trustgraph-cli/trustgraph/cli/list_explain_traces.py` | Lista sessões de agente junto com GraphRAG |
## Arquivos Criados
| Arquivo | Propósito |
|------|---------|
| `trustgraph-base/trustgraph/provenance/agent.py` | Geradores de triplas específicos para agente |
## Atualizações da CLI
**Detecção:** Tanto GraphRAG quanto Agent Questions têm o tipo `tg:Question`. Distinguido por:
1. Padrão de URI: `urn:trustgraph:agent:` vs `urn:trustgraph:question:`
2. Entidades derivadas: `tg:Analysis` (agente) vs `tg:Exploration` (GraphRAG)
**`list_explain_traces.py`:**
- Mostra a coluna Tipo (Agente vs GraphRAG)
**`show_explain_trace.py`:**
- Detecta automaticamente o tipo de rastreamento
- A renderização do agente mostra: Pergunta → Etapa(s) de análise → Conclusão
## Compatibilidade com versões anteriores
- `session_id` padrão é `""` - solicitações antigas funcionam, mas não terão procedência
- `collection` padrão é `"default"` - fallback razoável
- A CLI lida graciosamente com ambos os tipos de rastreamento
## Verificação
```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"
```
## Trabalhos Futuros (Não Neste PR)
- Dependências de DAG (quando a análise N usa resultados de várias análises anteriores)
- Vinculação de rastreabilidade específica da ferramenta (KnowledgeQuery → seu rastreamento GraphRAG)
- Emissão de rastreabilidade em fluxo (emitir continuamente, não em lote no final)

View file

@ -0,0 +1,113 @@
---
layout: default
title: "Arquitetura de Grafos de Conhecimento: Fundamentos"
parent: "Portuguese (Beta)"
---
# Arquitetura de Grafos de Conhecimento: Fundamentos
> **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 Grafo Sujeito-Predicado-Objeto (SPO)
**Decisão**: Adotar SPO/RDF como o modelo central de representação de conhecimento
**Justificativa**:
- Fornece máxima flexibilidade e interoperabilidade com tecnologias de grafos existentes
- Permite a tradução perfeita para outras linguagens de consulta de grafos (por exemplo, SPO → Cypher, mas não o contrário)
- Cria uma base que "desbloqueia muitas" capacidades subsequentes
- Suporta relacionamentos de nó para nó (SPO) e relacionamentos de nó para literal (RDF)
**Implementação**:
- Estrutura de dados principal: `node → edge → {node | literal}`
- Manter a compatibilidade com os padrões RDF, ao mesmo tempo em que suporta operações SPO estendidas
## Fundamento 2: Integração Nativa de Grafos de Conhecimento com LLMs
**Decisão**: Otimizar a estrutura e as operações do grafo de conhecimento para a interação com LLMs
**Justificativa**:
- O caso de uso primário envolve LLMs interagindo com grafos de conhecimento
- As escolhas da tecnologia de grafos devem priorizar a compatibilidade com LLMs em vez de outras considerações
- Permite fluxos de trabalho de processamento de linguagem natural que aproveitam o conhecimento estruturado
**Implementação**:
- Projetar esquemas de grafo que os LLMs possam entender e usar efetivamente
- Otimizar para padrões comuns de interação com LLMs
## Fundamento 3: Navegação de Grafos Baseada em Incorporações
**Decisão**: Implementar um mapeamento direto de consultas de linguagem natural para nós de grafos por meio de incorporações
**Justificativa**:
- Permite o caminho mais simples possível de uma consulta de PNL para a navegação no grafo
- Evita etapas complexas de geração de consultas intermediárias
- Fornece capacidades de pesquisa semântica eficientes dentro da estrutura do grafo
**Implementação**:
- `NLP Query → Graph Embeddings → Graph Nodes`
- Manter representações de incorporação para todas as entidades do grafo
- Suporte para correspondência de similaridade semântica direta para a resolução de consultas
## Fundamento 4: Resolução Distribuída de Entidades com Identificadores Determinísticos
**Decisão**: Suportar a extração de conhecimento paralela com identificação determinística de entidades (regra dos 80%)
**Justificativa**:
- **Ideal**: A extração em um único processo com visibilidade completa do estado permite a resolução perfeita de entidades
- **Realidade**: Os requisitos de escalabilidade exigem capacidades de processamento paralelo
- **Compromisso**: Projetar para identificação determinística de entidades em processos distribuídos
**Implementação**:
- Desenvolver mecanismos para gerar identificadores consistentes e exclusivos em diferentes extratores de conhecimento
- A mesma entidade mencionada em processos diferentes deve resolver para o mesmo identificador
- Reconhecer que ~20% dos casos extremos podem exigir modelos de processamento alternativos
- Projetar mecanismos de fallback para cenários complexos de resolução de entidades
## Fundamento 5: Arquitetura Orientada a Eventos com Publicação-Subscrição
**Decisão**: Implementar um sistema de mensagens pub-sub para a coordenação do sistema
**Justificativa**:
- Permite o acoplamento frouxo entre os componentes de extração, armazenamento e consulta de conhecimento
- Suporta atualizações e notificações em tempo real em todo o sistema
- Facilita fluxos de trabalho de processamento distribuídos e escaláveis
**Implementação**:
- Coordenação orientada a mensagens entre os componentes do sistema
- Streams de eventos para atualizações de conhecimento, conclusão da extração e resultados de consultas
## Fundamento 6: Comunicação de Agentes Reentrantes
**Decisão**: Suportar operações pub-sub reentrantes para o processamento baseado em agentes
**Justificativa**:
- Permite fluxos de trabalho sofisticados de agentes, nos quais os agentes podem acionar e responder uns aos outros
- Suporta pipelines complexos de processamento de conhecimento de várias etapas
- Permite padrões de processamento recursivos e iterativos
**Implementação**:
- O sistema pub-sub deve lidar com chamadas reentrantes com segurança
- Mecanismos de coordenação de agentes que evitam loops infinitos
- Suporte para orquestração de fluxos de trabalho de agentes
## Fundamento 7: Integração com Armazenamento de Dados Colunares
**Decisão**: Garantir a compatibilidade das consultas com sistemas de armazenamento colunar.
**Justificativa**:
- Permite consultas analíticas eficientes em grandes conjuntos de dados de conhecimento.
- Suporta casos de uso de inteligência de negócios e relatórios.
- Integra a representação de conhecimento baseada em grafos com fluxos de trabalho analíticos tradicionais.
**Implementação**:
- Camada de tradução de consultas: Consultas de grafos → Consultas colunares.
- Estratégia de armazenamento híbrida que suporta tanto operações de grafos quanto cargas de trabalho analíticas.
- Manter o desempenho das consultas em ambos os paradigmas.
---
## Resumo dos Princípios da Arquitetura
1. **Flexibilidade em Primeiro Lugar**: O modelo SPO/RDF fornece a máxima adaptabilidade.
2. **Otimização para LLM**: Todas as decisões de design consideram os requisitos de interação com LLM.
3. **Eficiência Semântica**: Mapeamento direto de embeddings para nós para desempenho ideal da consulta.
4. **Escalabilidade Pragmática**: Equilibrar a precisão perfeita com o processamento distribuído prático.
5. **Coordenação Orientada a Eventos**: Pub-sub permite o acoplamento fraco e a escalabilidade.
6. **Compatível com Agentes**: Suporta fluxos de trabalho complexos de processamento multi-agentes.
7. **Compatibilidade Analítica**: Integra os paradigmas de grafos e colunares para consultas abrangentes.
Essas bases estabelecem uma arquitetura de grafo de conhecimento que equilibra a rigidez teórica com os requisitos práticos de escalabilidade, otimizada para a integração com LLM e o processamento distribuído.

View file

@ -0,0 +1,339 @@
---
layout: default
title: "Especificação Técnica: Consolidação da Configuração do Cassandra"
parent: "Portuguese (Beta)"
---
# Especificação Técnica: Consolidação da Configuração do 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.
**Status:** Rascunho
**Autor:** Assistente
**Data:** 2024-09-03
## Visão Geral
Esta especificação aborda os padrões de nomenclatura e configuração inconsistentes para os parâmetros de conexão do Cassandra em todo o código-fonte do TrustGraph. Atualmente, existem dois esquemas de nomenclatura de parâmetros diferentes (`cassandra_*` vs `graph_*`), o que leva à confusão e à complexidade da manutenção.
## Declaração do Problema
O código-fonte atualmente usa dois conjuntos distintos de parâmetros de configuração do Cassandra:
1. **Módulos Knowledge/Config/Library** usam:
`cassandra_host` (lista de hosts)
`cassandra_user`
`cassandra_password`
2. **Módulos Graph/Storage** usam:
`graph_host` (um único host, às vezes convertido em lista)
`graph_username`
`graph_password`
3. **Exposição inconsistente na linha de comando**:
Alguns processadores (por exemplo, `kg-store`) não expõem as configurações do Cassandra como argumentos de linha de comando
Outros processadores os expõem com nomes e formatos diferentes
O texto de ajuda não reflete os valores padrão das variáveis de ambiente
Ambos os conjuntos de parâmetros se conectam ao mesmo cluster Cassandra, mas com convenções de nomenclatura diferentes, causando:
Confusão na configuração para os usuários
Aumento da carga de manutenção
Documentação inconsistente
Potencial para configuração incorreta
Impossibilidade de substituir as configurações via linha de comando em alguns processadores
## Solução Proposta
### 1. Padronização dos Nomes dos Parâmetros
Todos os módulos usarão nomes de parâmetros `cassandra_*` consistentes:
`cassandra_host` - Lista de hosts (armazenada internamente como lista)
`cassandra_username` - Nome de usuário para autenticação
`cassandra_password` - Senha para autenticação
### 2. Argumentos da Linha de Comando
Todos os processadores DEVEM expor a configuração do Cassandra por meio de argumentos de linha de comando:
`--cassandra-host` - Lista separada por vírgulas de hosts
`--cassandra-username` - Nome de usuário para autenticação
`--cassandra-password` - Senha para autenticação
### 3. Fallback de Variáveis de Ambiente
Se os parâmetros da linha de comando não forem fornecidos explicitamente, o sistema verificará as variáveis de ambiente:
`CASSANDRA_HOST` - Lista separada por vírgulas de hosts
`CASSANDRA_USERNAME` - Nome de usuário para autenticação
`CASSANDRA_PASSWORD` - Senha para autenticação
### 4. Valores Padrão
Se nem os parâmetros da linha de comando nem as variáveis de ambiente forem especificados:
`cassandra_host` tem como padrão `["cassandra"]`
`cassandra_username` tem como padrão `None` (sem autenticação)
`cassandra_password` tem como padrão `None` (sem autenticação)
### 5. Requisitos do Texto de Ajuda
A saída `--help` deve:
Mostrar os valores das variáveis de ambiente como padrões quando definidos
Nunca exibir valores de senha (exibir `****` ou `<set>` em vez disso)
Indicar claramente a ordem de resolução no texto de ajuda
Exemplo de saída de ajuda:
```
--cassandra-host HOST
Cassandra host list, comma-separated (default: prod-cluster-1,prod-cluster-2)
[from CASSANDRA_HOST environment variable]
--cassandra-username USERNAME
Cassandra username (default: cassandra_user)
[from CASSANDRA_USERNAME environment variable]
--cassandra-password PASSWORD
Cassandra password (default: <set from environment>)
```
## Detalhes de Implementação
### Ordem de Resolução de Parâmetros
Para cada parâmetro do Cassandra, a ordem de resolução será:
1. Valor do argumento de linha de comando
2. Variável de ambiente (`CASSANDRA_*`)
3. Valor padrão
### Tratamento de Parâmetros de Host
O parâmetro `cassandra_host`:
A linha de comando aceita uma string separada por vírgulas: `--cassandra-host "host1,host2,host3"`
A variável de ambiente aceita uma string separada por vírgulas: `CASSANDRA_HOST="host1,host2,host3"`
Internamente sempre armazenado como uma lista: `["host1", "host2", "host3"]`
Host único: `"localhost"` → convertido para `["localhost"]`
Já é uma lista: `["host1", "host2"]` → usado como está
### Lógica de Autenticação
A autenticação será usada quando tanto `cassandra_username` quanto `cassandra_password` forem fornecidos:
```python
if cassandra_username and cassandra_password:
# Use SSL context and PlainTextAuthProvider
else:
# Connect without authentication
```
## Arquivos a serem modificados
### Módulos que utilizam parâmetros `graph_*` (a serem alterados):
`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
`trustgraph-flow/trustgraph/storage/objects/cassandra/write.py`
`trustgraph-flow/trustgraph/storage/rows/cassandra/write.py`
`trustgraph-flow/trustgraph/query/triples/cassandra/service.py`
### Módulos que utilizam parâmetros `cassandra_*` (a serem atualizados com fallback do ambiente):
`trustgraph-flow/trustgraph/tables/config.py`
`trustgraph-flow/trustgraph/tables/knowledge.py`
`trustgraph-flow/trustgraph/tables/library.py`
`trustgraph-flow/trustgraph/storage/knowledge/store.py`
`trustgraph-flow/trustgraph/cores/knowledge.py`
`trustgraph-flow/trustgraph/librarian/librarian.py`
`trustgraph-flow/trustgraph/librarian/service.py`
`trustgraph-flow/trustgraph/config/service/service.py`
`trustgraph-flow/trustgraph/cores/service.py`
### Arquivos de teste a serem atualizados:
`tests/unit/test_cores/test_knowledge_manager.py`
`tests/unit/test_storage/test_triples_cassandra_storage.py`
`tests/unit/test_query/test_triples_cassandra_query.py`
`tests/integration/test_objects_cassandra_integration.py`
## Estratégia de implementação
### Fase 1: Criar um utilitário de configuração comum
Crie funções utilitárias para padronizar a configuração do Cassandra em todos os processadores:
```python
import os
import argparse
def get_cassandra_defaults():
"""Get default values from environment variables or fallback."""
return {
'host': os.getenv('CASSANDRA_HOST', 'cassandra'),
'username': os.getenv('CASSANDRA_USERNAME'),
'password': os.getenv('CASSANDRA_PASSWORD')
}
def add_cassandra_args(parser: argparse.ArgumentParser):
"""
Add standardized Cassandra arguments to an argument parser.
Shows environment variable values in help text.
"""
defaults = get_cassandra_defaults()
# Format help text with env var indication
host_help = f"Cassandra host list, comma-separated (default: {defaults['host']})"
if 'CASSANDRA_HOST' in os.environ:
host_help += " [from CASSANDRA_HOST]"
username_help = f"Cassandra username"
if defaults['username']:
username_help += f" (default: {defaults['username']})"
if 'CASSANDRA_USERNAME' in os.environ:
username_help += " [from CASSANDRA_USERNAME]"
password_help = "Cassandra password"
if defaults['password']:
password_help += " (default: <set>)"
if 'CASSANDRA_PASSWORD' in os.environ:
password_help += " [from CASSANDRA_PASSWORD]"
parser.add_argument(
'--cassandra-host',
default=defaults['host'],
help=host_help
)
parser.add_argument(
'--cassandra-username',
default=defaults['username'],
help=username_help
)
parser.add_argument(
'--cassandra-password',
default=defaults['password'],
help=password_help
)
def resolve_cassandra_config(args) -> tuple[list[str], str|None, str|None]:
"""
Convert argparse args to Cassandra configuration.
Returns:
tuple: (hosts_list, username, password)
"""
# Convert host string to list
if isinstance(args.cassandra_host, str):
hosts = [h.strip() for h in args.cassandra_host.split(',')]
else:
hosts = args.cassandra_host
return hosts, args.cassandra_username, args.cassandra_password
```
### Fase 2: Atualizar Módulos Usando Parâmetros `graph_*`
1. Alterar os nomes dos parâmetros de `graph_*` para `cassandra_*`
2. Substituir os métodos personalizados `add_args()` por métodos padronizados `add_cassandra_args()`
3. Usar as funções auxiliares de configuração comuns
4. Atualizar as strings de documentação
Exemplo de transformação:
```python
# OLD CODE
@staticmethod
def add_args(parser):
parser.add_argument(
'-g', '--graph-host',
default="localhost",
help=f'Graph host (default: localhost)'
)
parser.add_argument(
'--graph-username',
default=None,
help=f'Cassandra username'
)
# NEW CODE
@staticmethod
def add_args(parser):
FlowProcessor.add_args(parser)
add_cassandra_args(parser) # Use standard helper
```
### Fase 3: Atualizar Módulos Usando Parâmetros `cassandra_*`
1. Adicionar suporte para argumentos de linha de comando onde estiver faltando (por exemplo, `kg-store`)
2. Substituir as definições de argumentos existentes por `add_cassandra_args()`
3. Usar `resolve_cassandra_config()` para resolução consistente
4. Garantir o tratamento consistente da lista de hosts
### Fase 4: Atualizar Testes e Documentação
1. Atualizar todos os arquivos de teste para usar os novos nomes de parâmetros
2. Atualizar a documentação da CLI
3. Atualizar a documentação da API
4. Adicionar documentação de variáveis de ambiente
## Compatibilidade com Versões Anteriores
Para manter a compatibilidade com versões anteriores durante a transição:
1. **Avisos de descontinuação** para parâmetros `graph_*`
2. **Aliasing de parâmetros** - aceitar tanto os nomes antigos quanto os novos inicialmente
3. **Implementação gradual** ao longo de várias versões
4. **Atualizações na documentação** com um guia de migração
Exemplo de código de compatibilidade com versões anteriores:
```python
def __init__(self, **params):
# Handle deprecated graph_* parameters
if 'graph_host' in params:
warnings.warn("graph_host is deprecated, use cassandra_host", DeprecationWarning)
params.setdefault('cassandra_host', params.pop('graph_host'))
if 'graph_username' in params:
warnings.warn("graph_username is deprecated, use cassandra_username", DeprecationWarning)
params.setdefault('cassandra_username', params.pop('graph_username'))
# ... continue with standard resolution
```
## Estratégia de Testes
1. **Testes unitários** para a lógica de resolução de configuração
2. **Testes de integração** com várias combinações de configuração
3. **Testes de variáveis de ambiente**
4. **Testes de compatibilidade retroativa** com parâmetros obsoletos
5. **Testes do Docker Compose** com variáveis de ambiente
## Atualizações da Documentação
1. Atualizar toda a documentação dos comandos da linha de comando
2. Atualizar a documentação da API
3. Criar um guia de migração
4. Atualizar os exemplos do Docker Compose
5. Atualizar a documentação de referência de configuração
## Riscos e Mitigações
| Risco | Impacto | Mitigação |
|------|--------|------------|
| Mudanças que podem afetar os usuários | Alto | Implementar um período de compatibilidade retroativa |
| Confusão na configuração durante a transição | Médio | Documentação clara e avisos de descontinuação |
| Falhas nos testes | Médio | Atualizações abrangentes nos testes |
| Problemas de implantação do Docker | Alto | Atualizar todos os exemplos do Docker Compose |
## Critérios de Sucesso
[ ] Todos os módulos usam nomes de parâmetros `cassandra_*` consistentes
[ ] Todos os processadores expõem as configurações do Cassandra por meio de argumentos da linha de comando
[ ] O texto de ajuda da linha de comando mostra os valores padrão das variáveis de ambiente
[ ] Os valores de senha nunca são exibidos no texto de ajuda
[ ] O fallback de variáveis de ambiente funciona corretamente
[ ] `cassandra_host` é tratado de forma consistente como uma lista internamente
[ ] A compatibilidade retroativa é mantida por pelo menos 2 versões
[ ] Todos os testes passam com o novo sistema de configuração
[ ] A documentação está totalmente atualizada
[ ] Os exemplos do Docker Compose funcionam com variáveis de ambiente
## Cronograma
**Semana 1:** Implementar o utilitário de configuração comum e atualizar os módulos `graph_*`
**Semana 2:** Adicionar suporte a variáveis de ambiente aos módulos `cassandra_*` existentes
**Semana 3:** Atualizar testes e documentação
**Semana 4:** Testes de integração e correção de bugs
## Considerações Futuras
Considerar a extensão desse padrão para outras configurações de banco de dados (por exemplo, Elasticsearch)
Implementar validação de configuração e melhores mensagens de erro
Adicionar suporte para configuração de pool de conexões do Cassandra
Considerar a adição de suporte para arquivos de configuração (.env)

View file

@ -0,0 +1,687 @@
---
layout: default
title: "Especificação Técnica: Refatoração de Desempenho da Base de Conhecimento Cassandra"
parent: "Portuguese (Beta)"
---
# Especificação Técnica: Refatoração de Desempenho da Base de Conhecimento 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.
**Status:** Rascunho
**Autor:** Assistente
**Data:** 2025-09-18
## Visão Geral
Esta especificação aborda problemas de desempenho na implementação da base de conhecimento Cassandra TrustGraph e propõe otimizações para o armazenamento e consulta de triplas RDF.
## Implementação Atual
### Design do Esquema
A implementação atual utiliza um design de tabela única em `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 Secundários:**
`triples_s` EM `s` (sujeito)
`triples_p` EM `p` (predicado)
`triples_o` EM `o` (objeto)
### Padrões de Consulta
A implementação atual suporta 8 padrões de consulta distintos:
1. **get_all(coleção, limite=50)** - Recupera todas as triplas para uma coleção
```sql
SELECT s, p, o FROM triples WHERE collection = ? LIMIT 50
```
2. **get_s(collection, s, limit=10)** - Consulta por assunto
```sql
SELECT p, o FROM triples WHERE collection = ? AND s = ? LIMIT 10
```
3. **get_p(collection, p, limit=10)** - Consulta por predicado
```sql
SELECT s, o FROM triples WHERE collection = ? AND p = ? LIMIT 10
```
4. **get_o(collection, o, limit=10)** - Consulta por objeto
```sql
SELECT s, p FROM triples WHERE collection = ? AND o = ? LIMIT 10
```
5. **get_sp(collection, s, p, limit=10)** - Consulta por sujeito + 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 + sujeito ⚠️
```sql
SELECT p FROM triples WHERE collection = ? AND o = ? AND s = ? LIMIT 10 ALLOW FILTERING
```
8. **get_spo(collection, s, p, o, limit=10)** - Correspondência exata de tripla.
```sql
SELECT s as x FROM triples WHERE collection = ? AND s = ? AND p = ? AND o = ? LIMIT 10
```
### Arquitetura Atual
**Arquivo: `trustgraph-flow/trustgraph/direct/cassandra_kg.py`**
Classe única `KnowledgeGraph` que gerencia todas as operações
Pool de conexões através de uma lista global `_active_clusters`
Nome de tabela fixo: `"triples"`
Modelo de keyspace por usuário
Replicação SimpleStrategy com fator 1
**Pontos de Integração:**
**Caminho de Escrita:** `trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
**Caminho de Consulta:** `trustgraph-flow/trustgraph/query/triples/cassandra/service.py`
**Armazenamento de Conhecimento:** `trustgraph-flow/trustgraph/tables/knowledge.py`
## Problemas de Desempenho Identificados
### Problemas no Nível do Schema
1. **Design de Chave Primária Ineficiente**
Atual: `PRIMARY KEY (collection, s, p, o)`
Resulta em agrupamento inadequado para padrões de acesso comuns
Força o uso de índices secundários caros
2. **Uso Excessivo de Índices Secundários** ⚠️
Três índices secundários em colunas de alta cardinalidade (s, p, o)
Índices secundários em Cassandra são caros e não escalam bem
As consultas 6 e 7 requerem `ALLOW FILTERING`, indicando modelagem de dados inadequada
3. **Risco de Partições Quentes**
Uma única chave de partição `collection` pode criar partições quentes
Grandes coleções se concentrarão em nós únicos
Não há estratégia de distribuição para balanceamento de carga
### Problemas no Nível da Consulta
1. **Uso de ALLOW FILTERING** ⚠️
Dois tipos de consulta (get_po, get_os) requerem `ALLOW FILTERING`
Essas consultas escaneiam várias partições e são extremamente caras
O desempenho degrada linearmente com o tamanho dos dados
2. **Padrões de Acesso Ineficientes**
Não há otimização para padrões de consulta RDF comuns
Índices compostos ausentes para combinações de consulta frequentes
Não há consideração para padrões de travessia de grafos
3. **Falta de Otimização de Consulta**
Não há cache de prepared statements
Não há dicas de consulta ou estratégias de otimização
Não há consideração para paginação além de um simples LIMIT
## Declaração do Problema
A implementação atual da base de conhecimento Cassandra tem dois gargalos críticos de desempenho:
### 1. Desempenho Ineficiente da Consulta get_po
A consulta `get_po(collection, p, o)` é extremamente ineficiente devido à necessidade de `ALLOW FILTERING`:
```sql
SELECT s FROM triples WHERE collection = ? AND p = ? AND o = ? LIMIT 10 ALLOW FILTERING
```
**Por que isso é problemático:**
`ALLOW FILTERING` força o Cassandra a escanear todas as partições dentro da coleção.
O desempenho diminui linearmente com o tamanho dos dados.
Este é um padrão de consulta RDF comum (encontrar sujeitos que têm um relacionamento específico de predicado-objeto).
Cria uma carga significativa no cluster à medida que os dados crescem.
### 2. Estratégia de Clustering Inadequada
A chave primária atual `PRIMARY KEY (collection, s, p, o)` oferece benefícios mínimos de clustering:
**Problemas com o clustering atual:**
`collection` como chave de partição não distribui os dados de forma eficaz.
A maioria das coleções contém dados diversos, tornando o clustering ineficaz.
Não há consideração para padrões de acesso comuns em consultas RDF.
Coleções grandes criam partições quentes em nós únicos.
As colunas de clustering (s, p, o) não otimizam para padrões típicos de travessia de grafos.
**Impacto:**
As consultas não se beneficiam da localidade dos dados.
Utilização inadequada do cache.
Distribuição desigual de carga entre os nós do cluster.
Gargalos de escalabilidade à medida que as coleções crescem.
## Solução Proposta: Estratégia de Desnormalização de 4 Tabelas
### Visão Geral
Substitua a única tabela `triples` por quatro tabelas projetadas especificamente, cada uma otimizada para padrões de consulta específicos. Isso elimina a necessidade de índices secundários e ALLOW FILTERING, ao mesmo tempo em que fornece desempenho ideal para todos os tipos de consulta. A quarta tabela permite a exclusão eficiente de coleções, apesar das chaves de partição compostas.
### Novo Design de Esquema
**Tabela 1: Consultas Centradas no Sujeito (triples_s)**
```sql
CREATE TABLE triples_s (
collection text,
s text,
p text,
o text,
PRIMARY KEY ((collection, s), p, o)
);
```
**Otimiza:** get_s, get_sp, get_os
**Chave de Partição:** (coleção, s) - Melhor distribuição do que apenas a coleção
**Agrupamento:** (p, o) - Permite pesquisas eficientes de predicados/objetos para um sujeito
**Tabela 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)
);
```
**Otimiza:** get_p, get_po (elimina ALLOW FILTERING!)
**Chave de Partição:** (coleção, p) - Acesso direto por predicado
**Agrupamento:** (o, s) - Traversal eficiente de objeto-sujeito
**Tabela 3: Consultas Orientadas a Objetos (triples_o)**
```sql
CREATE TABLE triples_o (
collection text,
o text,
s text,
p text,
PRIMARY KEY ((collection, o), s, p)
);
```
**Otimiza:** get_o
**Chave de Partição:** (coleção, o) - Acesso direto por objeto
**Agrupamento:** (s, p) - Traversal eficiente de sujeito-predicado
**Tabela 4: Gerenciamento de Coleções e Consultas SPO (triples_collection)**
```sql
CREATE TABLE triples_collection (
collection text,
s text,
p text,
o text,
PRIMARY KEY (collection, s, p, o)
);
```
**Otimiza:** get_spo, delete_collection
**Chave de Partição:** coleção apenas - Permite operações eficientes no nível da coleção.
**Agrupamento:** (s, p, o) - Ordenação padrão de triplas.
**Propósito:** Uso duplo para pesquisas exatas de SPO e como índice de exclusão.
### Mapeamento de Consultas
| Consulta Original | Tabela de Destino | Melhoria de Desempenho |
|----------------|-------------|------------------------|
| get_all(collection) | triples_s | PERMITE FILTRAGEM (aceitável para varredura) |
| get_s(collection, s) | triples_s | Acesso direto à partição |
| get_p(collection, p) | triples_p | Acesso direto à partição |
| get_o(collection, o) | triples_o | Acesso direto à partição |
| get_sp(collection, s, p) | triples_s | Partição + agrupamento |
| get_po(collection, p, o) | triples_p | **Não mais PERMITE FILTRAGEM!** |
| get_os(collection, o, s) | triples_o | Partição + agrupamento |
| get_spo(collection, s, p, o) | triples_collection | Pesquisa de chave exata |
| delete_collection(collection) | triples_collection | Lê o índice, exclusão em lote de todos |
### Estratégia de Exclusão de Coleção
Com chaves de partição compostas, não podemos simplesmente executar `DELETE FROM table WHERE collection = ?`. Em vez disso:
1. **Fase de Leitura:** Consulta `triples_collection` para enumerar todas as triplas:
```sql
SELECT s, p, o FROM triples_collection WHERE collection = ?
```
Isso é eficiente, já que `collection` é a chave de partição para esta tabela.
2. **Fase de Exclusão:** Para cada tripla (s, p, o), exclua de todas as 4 tabelas usando as chaves de partição 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 em lotes de 100 para maior eficiência.
**Análise de Compromissos:**
✅ Mantém o desempenho ideal das consultas com partições distribuídas.
✅ Sem partições com alta carga para grandes coleções.
❌ Lógica de exclusão mais complexa (leitura e depois exclusão).
❌ Tempo de exclusão proporcional ao tamanho da coleção.
### Benefícios
1. **Elimina ALLOW FILTERING** - Cada consulta tem um caminho de acesso ideal (exceto a varredura get_all).
2. **Sem Índices Secundários** - Cada tabela É o índice para seu padrão de consulta.
3. **Melhor Distribuição de Dados** - As chaves de partição compostas distribuem a carga de forma eficaz.
4. **Desempenho Previsível** - O tempo de consulta é proporcional ao tamanho do resultado, não ao tamanho total dos dados.
5. **Aproveita os Pontos Fortes do Cassandra** - Projetado para a arquitetura do Cassandra.
6. **Permite a Exclusão de Coleções** - triples_collection serve como índice de exclusão.
## Plano de Implementação
### Arquivos que Requerem Alterações
#### Arquivo de Implementação Primário
**`trustgraph-flow/trustgraph/direct/cassandra_kg.py`** - Reescrita completa necessária.
**Métodos Atuais a Serem Refatorados:**
```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
```
#### Arquivos de Integração (Nenhuma Alteração de Lógica Necessária)
**`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`**
Nenhuma alteração necessária - utiliza a API KnowledgeGraph existente
Beneficia automaticamente das melhorias de desempenho
**`trustgraph-flow/trustgraph/query/triples/cassandra/service.py`**
Nenhuma alteração necessária - utiliza a API KnowledgeGraph existente
Beneficia automaticamente das melhorias de desempenho
### Arquivos de Teste que Requerem Atualizações
#### Testes Unitários
**`tests/unit/test_storage/test_triples_cassandra_storage.py`**
Atualizar as expectativas dos testes para as alterações no esquema
Adicionar testes para a consistência de várias tabelas
Verificar se não há ALLOW FILTERING nos planos de consulta
**`tests/unit/test_query/test_triples_cassandra_query.py`**
Atualizar as asserções de desempenho
Testar todos os 8 padrões de consulta contra as novas tabelas
Verificar o roteamento da consulta para as tabelas corretas
#### Testes de Integração
**`tests/integration/test_cassandra_integration.py`**
Testes de ponta a ponta com o novo esquema
Comparativos de benchmarking de desempenho
Verificação da consistência dos dados entre as tabelas
**`tests/unit/test_storage/test_cassandra_config_integration.py`**
Atualizar os testes de validação de esquema
Testar cenários de migração
### Estratégia de Implementação
#### Fase 1: Esquema e Métodos Principais
1. **Reescrever o método `init()`** - Criar quatro tabelas em vez de uma
2. **Reescrever o método `insert()`** - Gravações em lote em todas as quatro tabelas
3. **Implementar instruções preparadas** - Para desempenho ideal
4. **Adicionar lógica de roteamento de tabela** - Direcionar consultas para as tabelas mais adequadas
5. **Implementar a exclusão de coleções** - Ler de triples_collection, excluir em lote de todas as tabelas
#### Fase 2: Otimização do Método de Consulta
1. **Reescrever cada método get_*** para usar a tabela mais adequada
2. **Remover todo o uso de ALLOW FILTERING**
3. **Implementar o uso eficiente da chave de agrupamento**
4. **Adicionar registro de desempenho da consulta**
#### Fase 3: Gerenciamento de Coleções
1. **Atualizar `delete_collection()`** - Remover de todas as três tabelas
2. **Adicionar verificação de consistência** - Garantir que todas as tabelas permaneçam sincronizadas
3. **Implementar operações em lote** - Para operações multi-tabela atômicas
### Detalhes Chave da Implementação
#### Estratégia de Gravação em Lote
```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 Roteamento 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 Exclusão de Coleções
```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}")
```
#### Otimização de Instruções 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
```
## Estratégia de Migração
### Abordagem de Migração de Dados
#### Opção 1: Implantação Blue-Green (Recomendada)
1. **Implantar o novo esquema junto com o existente** - Use nomes de tabelas diferentes temporariamente
2. **Período de escrita dupla** - Escrever tanto no esquema antigo quanto no novo durante a transição
3. **Migração em segundo plano** - Copiar dados existentes para novas tabelas
4. **Alternar leituras** - Direcionar consultas para novas tabelas assim que os dados forem migrados
5. **Remover tabelas antigas** - Após o período de verificação
#### Opção 2: Migração no Local
1. **Adição de esquema** - Criar novas tabelas no keyspace existente
2. **Script de migração de dados** - Copiar em lote da tabela antiga para as novas tabelas
3. **Atualização do aplicativo** - Implantar novo código após a conclusão da migração
4. **Limpeza da tabela antiga** - Remover a tabela antiga e os índices
### Compatibilidade com Versões Anteriores
#### Estratégia de Implantação
```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 Migração
```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)
```
### Estratégia de Validação
#### Verificações de Consistência de Dados
```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}"
```
## Estratégia de Testes
### Testes de Desempenho
#### Cenários de Benchmark
1. **Comparação de Desempenho de Consultas**
Métricas de desempenho antes/depois para todos os 8 tipos de consulta
Foco na melhoria de desempenho de `get_po` (eliminar `ALLOW FILTERING`)
Medir a latência da consulta sob vários tamanhos de dados
2. **Testes de Carga**
Execução de consultas concorrentes
Taxa de transferência de escrita com operações em lote
Utilização de memória e CPU
3. **Testes de Escalabilidade**
Desempenho com tamanhos de coleção crescentes
Distribuição de consultas em várias coleções
Utilização de nós do cluster
#### Conjuntos de Dados de Teste
**Pequeno:** 10K triplas por coleção
**Médio:** 100K triplas por coleção
**Grande:** 1M+ triplas por coleção
**Múltiplas coleções:** Testar a distribuição de partições
### Testes Funcionais
#### Atualizações de Testes Unitários
```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')
```
#### Atualizações do Teste de Integração
```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
```
### Plano de Reversão
#### Estratégia de Reversão Rápida
1. **Alternância de variáveis de ambiente** - Retorne imediatamente para as tabelas legadas.
2. **Mantenha as tabelas legadas** - Não as exclua até que o desempenho seja comprovado.
3. **Alertas de monitoramento** - Disparos de reversão automatizados com base em taxas de erro/latência.
#### Validação da Reversão
```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()
```
## Riscos e Considerações
### Riscos de Desempenho
**Aumento da latência de escrita** - 4 operações de escrita por inserção (33% a mais do que a abordagem de 3 tabelas)
**Sobrecarga de armazenamento** - 4x o requisito de armazenamento (33% a mais do que a abordagem de 3 tabelas)
**Falhas de escrita em lote** - Necessidade de tratamento adequado de erros
**Complexidade da exclusão** - A exclusão da coleção requer um loop de leitura e exclusão
### Riscos Operacionais
**Complexidade da migração** - Migração de dados para grandes conjuntos de dados
**Desafios de consistência** - Garantir que todas as tabelas permaneçam sincronizadas
**Lacunas de monitoramento** - Necessidade de novas métricas para operações de várias tabelas
### Estratégias de Mitigação
1. **Implantação gradual** - Começar com pequenas coleções
2. **Monitoramento abrangente** - Rastrear todas as métricas de desempenho
3. **Validação automatizada** - Verificação contínua de consistência
4. **Capacidade de reversão rápida** - Seleção de tabela baseada no ambiente
## Critérios de Sucesso
### Melhorias de Desempenho
[ ] **Eliminar ALLOW FILTERING** - as consultas get_po e get_os são executadas sem filtragem
[ ] **Redução da latência da consulta** - melhoria de 50% ou mais nos tempos de resposta da consulta
[ ] **Melhor distribuição de carga** - Sem partições quentes, distribuição uniforme entre os nós do cluster
[ ] **Desempenho escalável** - Tempo de consulta proporcional ao tamanho do resultado, não ao volume total de dados
### Requisitos Funcionais
[ ] **Compatibilidade da API** - Todo o código existente continua a funcionar sem alterações
[ ] **Consistência de dados** - As três tabelas permanecem sincronizadas
[ ] **Nenhuma perda de dados** - A migração preserva todas as triplas existentes
[ ] **Compatibilidade com versões anteriores** - Capacidade de reverter para o esquema legado
### Requisitos Operacionais
[ ] **Migração segura** - Implantação blue-green com capacidade de reversão
[ ] **Cobertura de monitoramento** - Métricas abrangentes para operações de várias tabelas
[ ] **Cobertura de teste** - Todos os padrões de consulta testados com benchmarks de desempenho
[ ] **Documentação** - Procedimentos de implantação e operação atualizados
## Cronograma
### Fase 1: Implementação
[ ] Reescrever `cassandra_kg.py` com o esquema de várias tabelas
[ ] Implementar operações de escrita em lote
[ ] Adicionar otimização de declaração preparada
[ ] Atualizar testes unitários
### Fase 2: Testes de Integração
[ ] Atualizar testes de integração
[ ] Benchmarking de desempenho
[ ] Teste de carga com volumes de dados realistas
[ ] Scripts de validação para consistência de dados
### Fase 3: Planejamento da Migração
[ ] Scripts de implantação blue-green
[ ] Ferramentas de migração de dados
[ ] Atualizações do painel de monitoramento
[ ] Procedimentos de reversão
### Fase 4: Implantação em Produção
[ ] Implantação gradual em produção
[ ] Monitoramento e validação de desempenho
[ ] Limpeza de tabelas legadas
[ ] Atualizações de documentação
## Conclusão
Esta estratégia de desnormalização multi-tabela aborda diretamente os dois gargalos de desempenho críticos:
1. **Elimina o ALLOW FILTERING caro** fornecendo estruturas de tabela ideais para cada padrão de consulta
2. **Melhora a eficácia do agrupamento** por meio de chaves de partição compostas que distribuem a carga adequadamente
A abordagem aproveita os pontos fortes do Cassandra, mantendo a compatibilidade total da API, garantindo que o código existente se beneficie automaticamente das melhorias de desempenho.

View file

@ -0,0 +1,410 @@
---
layout: default
title: "Especificação Técnica de Gerenciamento de Coleções"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Gerenciamento de Coleções
> **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.
## Visão Geral
Esta especificação descreve as capacidades de gerenciamento de coleções para o TrustGraph, exigindo a criação explícita de coleções e fornecendo controle direto sobre o ciclo de vida da coleção. As coleções devem ser criadas explicitamente antes de serem usadas, garantindo a sincronização adequada entre os metadados do bibliotecário e todos os backends de armazenamento. O recurso suporta quatro casos de uso primários:
1. **Criação de Coleção**: Crie explicitamente coleções antes de armazenar dados
2. **Listagem de Coleções**: Visualize todas as coleções existentes no sistema
3. **Gerenciamento de Metadados da Coleção**: Atualize nomes, descrições e tags de coleções
4. **Exclusão de Coleções**: Remova coleções e seus dados associados em todos os tipos de armazenamento
## Objetivos
**Criação Explícita de Coleção**: Exigir que as coleções sejam criadas antes que os dados possam ser armazenados
**Sincronização de Armazenamento**: Garantir que as coleções existam em todos os backends de armazenamento (vetores, objetos, triplas)
**Visibilidade da Coleção**: Permitir que os usuários listem e inspecionem todas as coleções em seu ambiente
**Limpeza de Coleções**: Permitir a exclusão de coleções que não são mais necessárias
**Organização de Coleções**: Suportar rótulos e tags para melhor rastreamento e descoberta de coleções
**Gerenciamento de Metadados**: Associar metadados significativos às coleções para clareza operacional
**Descoberta de Coleções**: Facilitar a localização de coleções específicas por meio de filtragem e pesquisa
**Transparência Operacional**: Fornecer visibilidade clara do ciclo de vida e do uso da coleção
**Gerenciamento de Recursos**: Permitir a limpeza de coleções não utilizadas para otimizar a utilização de recursos
**Integridade de Dados**: Impedir a criação de coleções órfãs no armazenamento sem rastreamento de metadados
## Contexto
Anteriormente, as coleções no TrustGraph eram criadas implicitamente durante as operações de carregamento de dados, levando a problemas de sincronização em que as coleções poderiam existir em backends de armazenamento sem metadados correspondentes no bibliotecário. Isso criava desafios de gerenciamento e dados órfãos.
O modelo de criação explícita de coleções aborda esses problemas:
Exigindo que as coleções sejam criadas antes de serem usadas via `tg-set-collection`
Transmitindo a criação da coleção para todos os backends de armazenamento
Mantendo um estado sincronizado entre os metadados do bibliotecário e o armazenamento
Impedindo a gravação em coleções inexistentes
Fornecendo gerenciamento claro do ciclo de vida da coleção
Esta especificação define o modelo de gerenciamento explícito de coleções. Ao exigir a criação explícita de coleções, o TrustGraph garante:
Que as coleções sejam rastreadas nos metadados do bibliotecário desde a criação
Que todos os backends de armazenamento estejam cientes das coleções antes de receber dados
Que não existam coleções órfãs no armazenamento
Visibilidade e controle operacionais claros sobre o ciclo de vida da coleção
Tratamento de erros consistente quando as operações referenciam coleções inexistentes
## Design Técnico
### Arquitetura
O sistema de gerenciamento de coleções será implementado dentro da infraestrutura existente do TrustGraph:
1. **Integração do Serviço do Bibliotecário**
As operações de gerenciamento de coleções serão adicionadas ao serviço do bibliotecário existente
Nenhum novo serviço é necessário - aproveita os padrões de autenticação e acesso existentes
Lida com a listagem, exclusão e gerenciamento de metadados de coleções
Módulo: trustgraph-librarian
2. **Tabela de Metadados de Coleção do Cassandra**
Nova tabela no keyspace existente do bibliotecário
Armazena metadados da coleção com acesso específico ao usuário
Chave primária: (user_id, collection_id) para multilocação adequada
Módulo: trustgraph-librarian
3. **CLI de Gerenciamento de Coleções**
Interface de linha de comando para operações de coleção
Fornece comandos de listagem, exclusão, rotulagem e gerenciamento de tags
Integra-se com o framework de CLI existente
Módulo: trustgraph-cli
### Modelos de Dados
#### Tabela de Metadados de Coleção do Cassandra
Os metadados da coleção serão armazenados em uma tabela estruturada do Cassandra no keyspace do bibliotecário:
```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)
);
```
Estrutura da tabela:
**user** + **collection**: Chave primária composta que garante o isolamento do usuário
**name**: Nome da coleção legível por humanos
**description**: Descrição detalhada do propósito da coleção
**tags**: Conjunto de tags para categorização e filtragem
**created_at**: Timestamp de criação da coleção
**updated_at**: Timestamp da última modificação
Esta abordagem permite:
Gerenciamento de coleções multi-tenant com isolamento de usuário
Consulta eficiente por usuário e coleção
Sistema de tags flexível para organização
Rastreamento do ciclo de vida para insights operacionais
#### Ciclo de Vida da Coleção
As coleções são explicitamente criadas no gerenciador antes que as operações de dados possam prosseguir:
1. **Criação da Coleção** (Dois Caminhos):
**Caminho A: Criação Iniciada pelo Usuário** via `tg-set-collection`:
O usuário fornece o ID da coleção, nome, descrição e tags
O gerenciador cria um registro de metadados na tabela `collections`
O gerenciador transmite "create-collection" para todos os backends de armazenamento
Todos os processadores de armazenamento criam a coleção e confirmam o sucesso
A coleção está agora pronta para operações de dados
**Caminho B: Criação Automática na Submissão de Documento**:
O usuário submete um documento especificando um ID de coleção
O gerenciador verifica se a coleção existe na tabela de metadados
Se não existir: O gerenciador cria metadados com valores padrão (nome=collection_id, descrição/tags vazias)
O gerenciador transmite "create-collection" para todos os backends de armazenamento
Todos os processadores de armazenamento criam a coleção e confirmam o sucesso
O processamento do documento prossegue com a coleção agora estabelecida
Ambos os caminhos garantem que a coleção exista nos metadados do gerenciador E em todos os backends de armazenamento antes de permitir operações de dados.
2. **Validação de Armazenamento**: As operações de escrita validam se a coleção existe:
Os processadores de armazenamento verificam o estado da coleção antes de aceitar escritas
As escritas em coleções inexistentes retornam um erro
Isso impede que as escritas contornem a lógica de criação de coleção do gerenciador
3. **Comportamento da Consulta**: As operações de consulta lidam com coleções inexistentes de forma elegante:
As consultas em coleções inexistentes retornam resultados vazios
Nenhum erro é lançado para operações de consulta
Permite a exploração sem exigir que a coleção exista
4. **Atualizações de Metadados**: Os usuários podem atualizar os metadados da coleção após a criação:
Atualize o nome, a descrição e as tags via `tg-set-collection`
As atualizações se aplicam apenas aos metadados do gerenciador
Os backends de armazenamento mantêm a coleção, mas as atualizações de metadados não são propagadas
5. **Exclusão Explícita**: Os usuários excluem coleções via `tg-delete-collection`:
O gerenciador transmite "delete-collection" para todos os backends de armazenamento
Aguarda a confirmação de todos os processadores de armazenamento
Exclui o registro de metadados do gerenciador somente após a limpeza do armazenamento estar completa
Garante que nenhum dado órfão permaneça no armazenamento
**Princípio Chave**: O gerenciador é o ponto de controle único para a criação de coleções. Seja iniciada por um comando do usuário ou pela submissão de um documento, o gerenciador garante o rastreamento adequado de metadados e a sincronização do backend de armazenamento antes de permitir operações de dados.
Operações necessárias:
**Criar Coleção**: Operação do usuário via `tg-set-collection` OU automática na submissão de documento
**Atualizar Metadados da Coleção**: Operação do usuário para modificar o nome, a descrição e as tags
**Excluir Coleção**: Operação do usuário para remover a coleção e seus dados em todos os armazenamentos
**Listar Coleções**: Operação do usuário para visualizar coleções com filtragem por tags
#### Gerenciamento de Coleções em Múltiplos Armazenamentos
As coleções existem em vários backends de armazenamento no TrustGraph:
**Vector Stores** (Qdrant, Milvus, Pinecone): Armazena embeddings e dados vetoriais
**Object Stores** (Cassandra): Armazena documentos e dados de arquivos
**Triple Stores** (Cassandra, Neo4j, Memgraph, FalkorDB): Armazena dados de grafo/RDF
Cada tipo de armazenamento implementa:
**Rastreamento do Estado da Coleção**: Manter o conhecimento de quais coleções existem
**Criação de Coleção**: Aceitar e processar operações de "criar-coleção"
**Validação de Coleção**: Verificar se a coleção existe antes de aceitar gravações
**Exclusão de Coleção**: Remover todos os dados para a coleção especificada
O serviço de bibliotecário coordena as operações de coleção em todos os tipos de armazenamento, garantindo:
Que as coleções sejam criadas em todos os backends antes de serem usadas
Que todos os backends confirmem a criação antes de retornar o sucesso
Ciclo de vida da coleção sincronizado em todos os tipos de armazenamento
Tratamento de erros consistente quando as coleções não existem
#### Rastreamento do Estado da Coleção por Tipo de Armazenamento
Cada backend de armazenamento rastreia o estado da coleção de forma diferente, com base em suas capacidades:
**Cassandra Triple Store:**
Usa a tabela `triples_collection` existente
Cria um triple de marcador do sistema quando a coleção é criada
Consulta: `SELECT collection FROM triples_collection WHERE collection = ? LIMIT 1`
Verificação de existência de coleção eficiente de partição única
**Qdrant/Milvus/Pinecone Vector Stores:**
As APIs nativas de coleção fornecem verificação de existência
Coleções criadas com configuração de vetor adequada
O método `collection_exists()` usa a API de armazenamento
A criação da coleção valida os requisitos de dimensão
**Neo4j/Memgraph/FalkorDB Graph Stores:**
Usa nós `:CollectionMetadata` para rastrear coleções
Propriedades do nó: `{user, collection, created_at}`
Consulta: `MATCH (c:CollectionMetadata {user: $user, collection: $collection})`
Separado de nós de dados para uma separação limpa
Permite listagem e validação eficientes de coleções
**Cassandra Object Store:**
Usa a tabela de metadados da coleção ou linhas de marcador
Padrão semelhante ao triple store
Valida a coleção antes das gravações de documentos
### APIs
APIs de Gerenciamento de Coleção (Bibliotecário):
**Criar/Atualizar Coleção**: Criar uma nova coleção ou atualizar metadados existentes via `tg-set-collection`
**Listar Coleções**: Recuperar coleções para um usuário com filtragem opcional por tag
**Excluir Coleção**: Remover a coleção e os dados associados, propagando para todos os tipos de armazenamento
APIs de Gerenciamento de Armazenamento (Todos os Processadores de Armazenamento):
**Criar Coleção**: Lidar com a operação de "criar-coleção", estabelecer a coleção no armazenamento
**Excluir Coleção**: Lidar com a operação de "excluir-coleção", remover todos os dados da coleção
**Verificação de Existência da Coleção**: Validação interna antes de aceitar operações de gravação
APIs de Operação de Dados (Comportamento Modificado):
**APIs de Gravação**: Validar se a coleção existe antes de aceitar dados, retornar um erro se não existir
**APIs de Consulta**: Retornar resultados vazios para coleções inexistentes sem erro
### Detalhes de Implementação
A implementação seguirá os padrões existentes do TrustGraph para integração de serviços e estrutura de comandos de linha de comando.
#### Exclusão em Cadeia de Coleção
Quando um usuário inicia a exclusão de uma coleção por meio do serviço de bibliotecário:
1. **Validação de Metadados**: Verificar se a coleção existe e se o usuário tem permissão para excluí-la
2. **Propagação para o Armazenamento**: O bibliotecário coordena a exclusão em todos os escritores de armazenamento:
Escritor de armazenamento vetorial: Remover incorporações e índices de vetor para o usuário e a coleção
Escritor de armazenamento de objetos: Remover documentos e arquivos para o usuário e a coleção
Escritor de triple store: Remover dados de grafo e triples para o usuário e a coleção
3. **Limpeza de Metadados**: Remover o registro de metadados da coleção do Cassandra
4. **Tratamento de Erros**: Se alguma exclusão de armazenamento falhar, manter a consistência por meio de mecanismos de reversão ou repetição
#### Interface de Gerenciamento de Coleção
**⚠️ ABORDAGEM ANTIGA - SUBSTITUÍDA POR PADRÃO BASEADO EM CONFIGURAÇÃO**
A arquitetura baseada em fila descrita abaixo foi substituída por uma abordagem baseada em configuração usando `CollectionConfigHandler`. Todos os backends de armazenamento agora recebem atualizações de coleção por meio de mensagens de configuração em vez de filas de gerenciamento dedicadas.
~~Todos os escritores de armazenamento implementam uma interface de gerenciamento de coleção padronizada com um esquema comum:~~
~~**Esquema de Mensagem (`StorageManagementRequest`):**~~
```json
{
"operation": "create-collection" | "delete-collection",
"user": "user123",
"collection": "documents-2024"
}
```
~~**Arquitetura de Filas:**~~
~~**Fila de Gerenciamento de Vetores** (`vector-storage-management`): Armazenamentos de vetores/embeddings~~
~~**Fila de Gerenciamento de Objetos** (`object-storage-management`): Armazenamentos de objetos/documentos~~
~~**Fila de Gerenciamento de Triplas** (`triples-storage-management`): Armazenamentos de grafos/RDF~~
~~**Fila de Resposta de Armazenamento** (`storage-management-response`): Todas as respostas são enviadas aqui~~
**Implementação Atual:**
Todos os backends de armazenamento agora usam `CollectionConfigHandler`:
**Integração de Configuração Push**: Os serviços de armazenamento se registram para notificações de configuração push
**Sincronização Automática**: Coleções criadas/excluídas com base em alterações de configuração
**Modelo Declarativo**: Coleções definidas no serviço de configuração, os backends sincronizam para corresponder
**Sem Requisição/Resposta**: Elimina a sobrecarga de coordenação e o rastreamento de respostas
**Rastreamento do Estado da Coleção**: Mantido via cache `known_collections`
**Operações Idempotentes**: É seguro processar a mesma configuração várias vezes
Cada backend de armazenamento implementa:
`create_collection(user: str, collection: str, metadata: dict)` - Criar estruturas de coleção
`delete_collection(user: str, collection: str)` - Remover todos os dados da coleção
`collection_exists(user: str, collection: str) -> bool` - Validar antes de gravar
#### Refatoração do Armazenamento de Triplas Cassandra
Como parte desta implementação, o armazenamento de triplas Cassandra será refatorado de um modelo de tabela por coleção para um modelo de tabela unificada:
**Arquitetura Atual:**
Keyspace por usuário, tabela separada por coleção
Esquema: `(s, p, o)` com `PRIMARY KEY (s, p, o)`
Nomes de tabela: coleções de usuários se tornam tabelas Cassandra separadas
**Nova Arquitetura:**
Keyspace por usuário, tabela única "triples" para todas as coleções
Esquema: `(collection, s, p, o)` com `PRIMARY KEY (collection, s, p, o)`
Isolamento de coleção por meio de particionamento de coleção
**Alterações Necessárias:**
1. **Refatoração da Classe TrustGraph** (`trustgraph/direct/cassandra.py`):
Remover o parâmetro `table` do construtor, usar a tabela "triples" fixa
Adicionar o parâmetro `collection` a todos os métodos
Atualizar o esquema para incluir a coleção como a primeira coluna
**Atualizações de Índice**: Novos índices serão criados para suportar todos os 8 padrões de consulta:
Índice em `(s)` para consultas baseadas em sujeito
Índice em `(p)` para consultas baseadas em predicado
Índice em `(o)` para consultas baseadas em objeto
Observação: O Cassandra não suporta índices secundários multi-coluna, portanto, estes são índices de coluna única
**Desempenho do Padrão de Consulta:**
`get_all()` - varredura de partição em `collection`
`get_s(s)` - usa a chave primária de forma eficiente (`collection, s`)
`get_p(p)` - usa `idx_p` com filtragem `collection`
`get_o(o)` - usa `idx_o` com filtragem `collection`
`get_sp(s, p)` - usa a chave primária de forma eficiente (`collection, s, p`)
⚠️ `get_po(p, o)` - requer `ALLOW FILTERING` (usa `idx_p` ou `idx_o` mais filtragem)
`get_os(o, s)` - usa `idx_o` com filtragem adicional em `s`
`get_spo(s, p, o)` - usa toda a chave primária de forma eficiente
**Observação sobre ALLOW FILTERING**: O padrão de consulta `get_po` requer `ALLOW FILTERING`, pois precisa tanto de restrições de predicado quanto de objeto sem um índice composto adequado. Isso é aceitável, pois este padrão de consulta é menos comum do que as consultas baseadas em sujeito no uso típico de armazenamentos de triplas.
2. **Atualizações do Gravador de Armazenamento** (`trustgraph/storage/triples/cassandra/write.py`):
Manter uma única conexão TrustGraph por usuário em vez de por (usuário, coleção)
Passar a coleção para as operações de inserção
Melhor utilização de recursos com menos conexões
3. **Atualizações do Serviço de Consulta** (`trustgraph/query/triples/cassandra/service.py`):
Uma única conexão TrustGraph por usuário
Passar a coleção para todas as operações de consulta
Manter a mesma lógica de consulta com o parâmetro de coleção
**Benefícios:**
**Exclusão Simplificada de Coleções**: Excluir usando a chave de partição `collection` em todas as 4 tabelas
**Eficiência de Recursos**: Menos conexões de banco de dados e objetos de tabela
**Operações entre Coleções**: Mais fácil de implementar operações que abrangem várias coleções
**Arquitetura Consistente**: Alinha-se com a abordagem unificada de metadados de coleção
**Validação de Coleção**: Fácil de verificar a existência da coleção via tabela `triples_collection`
As operações de coleção serão atômicas sempre que possível e fornecerão tratamento de erros e validação apropriados.
## Considerações de Segurança
As operações de gerenciamento de coleção requerem autorização apropriada para evitar acesso ou exclusão não autorizados de coleções. O controle de acesso estará alinhado com os modelos de segurança TrustGraph existentes.
## Considerações de Desempenho
As operações de listagem de coleções podem precisar de paginação para ambientes com um grande número de coleções. As consultas de metadados devem ser otimizadas para padrões de filtragem comuns.
## Estratégia de Testes
Os testes abrangentes cobrirão:
Fluxo de trabalho de criação de coleção de ponta a ponta
Sincronização do armazenamento de back-end
Validação de gravação para coleções inexistentes
Tratamento de consultas para coleções inexistentes
Exclusão de coleção em cascata em todos os armazenamentos
Cenários de tratamento de erros e recuperação
Testes unitários para cada armazenamento de back-end
Testes de integração para operações entre armazenamentos
## Status da Implementação
### ✅ Componentes Concluídos
1. **Serviço de Gerenciamento de Coleção Librarian** (`trustgraph-flow/trustgraph/librarian/collection_manager.py`)
Operações CRUD de metadados de coleção (listagem, atualização, exclusão)
Integração da tabela de metadados de coleção do Cassandra via `LibraryTableStore`
Coordenação da exclusão de coleção em cascata em todos os tipos de armazenamento
Tratamento de solicitações/respostas assíncronas com gerenciamento de erros adequado
2. **Esquema de Metadados de Coleção** (`trustgraph-base/trustgraph/schema/services/collection.py`)
Esquemas `CollectionManagementRequest` e `CollectionManagementResponse`
Esquema `CollectionMetadata` para registros de coleção
Definições de tópicos de fila de solicitações/respostas de coleção
3. **Esquema de Gerenciamento de Armazenamento** (`trustgraph-base/trustgraph/schema/services/storage.py`)
Esquemas `StorageManagementRequest` e `StorageManagementResponse`
Tópicos de fila de gerenciamento de armazenamento definidos
Formato de mensagem para operações de coleção no nível do armazenamento
4. **Esquema de 4 Tabelas do Cassandra** (`trustgraph-flow/trustgraph/direct/cassandra_kg.py`)
Chaves de partição compostas para desempenho da consulta
Tabela `triples_collection` para consultas SPO e rastreamento de exclusão
Exclusão de coleção implementada com o padrão de leitura e exclusão
### ✅ Migração para Padrão Baseado em Configuração - CONCLUÍDO
**Todos os armazenamentos de back-end foram migrados do padrão baseado em fila para o padrão baseado em configuração `CollectionConfigHandler`.**
Migrações concluídas:
`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 os backends agora:
Herdam de `CollectionConfigHandler`
Registram-se para notificações de push de configuração via `self.register_config_handler(self.on_collection_config)`
Implementam `create_collection(user, collection, metadata)` e `delete_collection(user, collection)`
Usam `collection_exists(user, collection)` para validar antes de gravar
Sincronizam automaticamente com as alterações do serviço de configuração
Infraestrutura baseada em fila herdada removida:
✅ Esquemas `StorageManagementRequest` e `StorageManagementResponse` removidos
✅ Definições de tópicos de fila de gerenciamento de armazenamento removidas
✅ Consumidor/produtor de gerenciamento de armazenamento removido de todos os backends
✅ Manipuladores `on_storage_management` removidos de todos os backends

View file

@ -0,0 +1,144 @@
---
layout: default
title: "Incorporações de Documentos - ID do Trecho"
parent: "Portuguese (Beta)"
---
# Incorporações de Documentos - ID do Trecho
> **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.
## Visão Geral
Atualmente, o armazenamento de incorporações de documentos armazena o texto do trecho diretamente no payload do armazenamento vetorial, duplicando dados que já existem no Garage. Esta especificação substitui o armazenamento do texto do trecho por referências `chunk_id`.
## Estado Atual
```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)
```
Payload do armazenamento vetorial:
```python
payload={"doc": chunk} # Duplicates Garage content
```
## Design
### Schema Changes
**ChunkEmbeddings** - substituir "chunk" por "chunk_id":
```python
@dataclass
class ChunkEmbeddings:
chunk_id: str = ""
vectors: list[list[float]] = field(default_factory=list)
```
**DocumentEmbeddingsResponse** - retornar chunk_ids em vez de chunks:
```python
@dataclass
class DocumentEmbeddingsResponse:
error: Error | None = None
chunk_ids: list[str] = field(default_factory=list)
```
### Carga Útil do Armazenamento Vetorial
Todos os armazenamentos (Qdrant, Milvus, Pinecone):
```python
payload={"chunk_id": chunk_id}
```
### Alterações no Processador de Documentos RAG
O processador de documentos RAG busca o conteúdo dos fragmentos do 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)
```
### Alterações na API/SDK
**DocumentEmbeddingsClient** retorna chunk_ids:
```python
return resp.chunk_ids # Changed from resp.chunks
```
**Formato de dados** (DocumentEmbeddingsResponseTranslator):
```python
result["chunk_ids"] = obj.chunk_ids # Changed from chunks
```
### Alterações na CLI
A ferramenta CLI exibe os chunk_ids (os chamadores podem buscar o conteúdo separadamente, se necessário).
## Arquivos a serem modificados
### Schema
`trustgraph-base/trustgraph/schema/knowledge/embeddings.py` - ChunkEmbeddings
`trustgraph-base/trustgraph/schema/services/query.py` - DocumentEmbeddingsResponse
### Mensagens/Tradutores
`trustgraph-base/trustgraph/messaging/translators/embeddings_query.py` - DocumentEmbeddingsResponseTranslator
### Cliente
`trustgraph-base/trustgraph/base/document_embeddings_client.py` - retornar chunk_ids
### SDK/API 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` - se aplicável
`trustgraph-base/trustgraph/api/bulk_client.py` - importar/exportar embeddings de documentos
`trustgraph-base/trustgraph/api/async_bulk_client.py` - importar/exportar embeddings de documentos
### Serviço de Embeddings
`trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py` - passar chunk_id
### Escritores de Armazenamento
`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`
### Serviços 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` - adicionar cliente librarian
`trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py` - buscar do 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`
## Benefícios
1. Única fonte de verdade - texto do chunk apenas no Garage
2. Redução do armazenamento do vetor
3. Permite a rastreabilidade em tempo de consulta via chunk_id

View file

@ -0,0 +1,675 @@
---
layout: default
title: "Especificação Técnica de Processamento em Lote de Embeddings"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Processamento em Lote 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.
## Visão Geral
Esta especificação descreve otimizações para o serviço de embeddings para suportar o processamento em lote de vários textos em um único pedido. A implementação atual processa um texto por vez, perdendo os benefícios de desempenho significativos que os modelos de embedding oferecem ao processar lotes.
1. **Ineficiência no Processamento de Texto Único**: A implementação atual envolve textos únicos em uma lista, subutilizando as capacidades de lote do FastEmbed.
2. **Sobrecarga de Pedido por Texto**: Cada texto requer uma viagem de ida e volta separada do Pulsar.
3. **Ineficiência na Inferência do Modelo**: Os modelos de embedding têm uma sobrecarga fixa por lote; lotes pequenos desperdiçam recursos de GPU/CPU.
4. **Processamento Serial nos Clientes**: Serviços importantes fazem loop sobre itens e chamam embeddings um de cada vez.
## Objetivos
**Suporte para API de Lote**: Permitir o processamento de vários textos em um único pedido.
**Compatibilidade com Versões Anteriores**: Manter o suporte para pedidos de texto único.
**Melhora Significativa no Desempenho**: Almejar uma melhora de desempenho de 5 a 10 vezes para operações em lote.
**Latência Reduzida por Texto**: Diminuir a latência amortizada ao incorporar vários textos.
**Eficiência de Memória**: Processar lotes sem consumo excessivo de memória.
**Independente do Provedor**: Suportar o processamento em lote em FastEmbed, Ollama e outros provedores.
**Migração do Cliente**: Atualizar todos os clientes de embedding para usar a API de lote sempre que for benéfico.
## Contexto
### Implementação Atual - Serviço de Embeddings
A implementação de embeddings em `trustgraph-flow/trustgraph/embeddings/fastembed/processor.py` apresenta uma ineficiência de desempenho 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. **Tamanho do Lote 1**: O método `embed()` do FastEmbed é otimizado para processamento em lote, mas sempre o chamamos com `[text]` - um lote de tamanho 1.
2. **Sobrecarga por Requisição**: Cada solicitação de incorporação incorre em:
Serialização/desserialização de mensagens Pulsar
Latência de ida e volta da rede
Sobrecarga de inicialização da inferência do modelo
Sobrecarga de agendamento assíncrono do Python
3. **Limitação do Esquema**: O esquema `EmbeddingsRequest` suporta apenas um único texto:
```python
@dataclass
class EmbeddingsRequest:
text: str = "" # Single text only
```
### Clientes Atuais - Processamento Serial
#### 1. Gateway de API
**Arquivo:** `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
O gateway aceita solicitações de incorporação de texto único via HTTP/WebSocket e as encaminha para o serviço de incorporação. Atualmente, não existe um endpoint para processamento em lote.
```python
class EmbeddingsRequestor(ServiceRequestor):
# Handles single EmbeddingsRequest -> EmbeddingsResponse
request_schema=EmbeddingsRequest, # Single text only
response_schema=EmbeddingsResponse,
```
**Impacto:** Clientes externos (aplicativos web, scripts) devem fazer N requisições HTTP para incorporar N textos.
#### 2. Serviço de Incorporação de Documentos
**Arquivo:** `trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py`
Processa blocos de documentos um de cada 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 trecho de documento requer uma chamada de embedding separada. Um documento com 100 trechos = 100 requisições de embedding.
#### 3. Serviço de Embeddings de Grafos
**Arquivo:** `trustgraph-flow/trustgraph/embeddings/graph_embeddings/embeddings.py`
Percorre as entidades e incorpora cada uma sequencialmente:
```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:** Uma mensagem com 50 entidades = 50 solicitações de incorporação serial. Isso é um grande gargalo durante a construção do grafo de conhecimento.
#### 4. Serviço de Incorporação de Linhas
**Arquivo:** `trustgraph-flow/trustgraph/embeddings/row_embeddings/embeddings.py`
Percorre textos únicos e incorpora cada um serialmente:
```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:** Processar uma tabela com 100 valores indexados únicos = 100 solicitações de incorporação seriais.
#### 5. EmbeddingsClient (Cliente Base)
**Arquivo:** `trustgraph-base/trustgraph/base/embeddings_client.py`
O cliente usado por todos os processadores de fluxo suporta apenas a incorporação 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 os clientes que utilizam este componente estão limitados a operações de texto único.
#### 6. Ferramentas de Linha de Comando
**Arquivo:** `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
A ferramenta de linha de comando aceita um ú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:** Os usuários não podem realizar incorporações em lote a partir da linha de comando. O processamento de um arquivo de textos requer N invocações.
#### 7. SDK Python
O SDK Python fornece duas classes de cliente para interagir com os serviços da TrustGraph. Ambas suportam apenas a incorporação de um único texto.
**Arquivo:** `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"]
```
**Arquivo:** `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:** Desenvolvedores Python que usam o SDK precisam iterar sobre textos e fazer N chamadas de API separadas. Não existe suporte para processamento em lote de embeddings para usuários do SDK.
### Impacto no Desempenho
Para ingestão típica de documentos (1000 trechos de texto):
**Atual:** 1000 requisições separadas, 1000 chamadas de inferência de modelo
**Em lote (batch_size=32):** 32 requisições, 32 chamadas de inferência de modelo (redução de 96,8%)
Para embedding de grafos (mensagem com 50 entidades):
**Atual:** 50 chamadas `await` sequenciais, ~5-10 segundos
**Em lote:** 1-2 chamadas em lote, ~0,5-1 segundo (melhora de 5-10x)
Bibliotecas como FastEmbed e similares alcançam escalabilidade quase linear no throughput com o tamanho do lote, até os limites do hardware (tipicamente 32-128 textos por lote).
## Design Técnico
### Arquitetura
A otimização de processamento em lote de embeddings requer alterações nos seguintes componentes:
#### 1. **Aprimoramento do Esquema**
Estender `EmbeddingsRequest` para suportar múltiplos textos
Estender `EmbeddingsResponse` para retornar múltiplos conjuntos de vetores
Manter a compatibilidade retroativa com requisições de texto único
Módulo: `trustgraph-base/trustgraph/schema/services/llm.py`
#### 2. **Aprimoramento do Serviço Base**
Atualizar `EmbeddingsService` para lidar com requisições em lote
Adicionar configuração do tamanho do lote
Implementar tratamento de requisições com suporte a lote
Módulo: `trustgraph-base/trustgraph/base/embeddings_service.py`
#### 3. **Atualizações do Processador do Provedor**
Atualizar o processador FastEmbed para passar o lote completo para `embed()`
Atualizar o processador Ollama para lidar com lotes (se suportado)
Adicionar processamento sequencial como fallback para provedores sem suporte a lote
Módulos:
`trustgraph-flow/trustgraph/embeddings/fastembed/processor.py`
`trustgraph-flow/trustgraph/embeddings/ollama/processor.py`
#### 4. **Aprimoramento do Cliente**
Adicionar método de embedding em lote para `EmbeddingsClient`
Suportar APIs de texto único e em lote
Adicionar agrupamento automático para grandes entradas
Módulo: `trustgraph-base/trustgraph/base/embeddings_client.py`
#### 5. **Atualizações do Chamador - Processadores de Fluxo**
Atualizar `graph_embeddings` para agrupar contextos de entidades
Atualizar `row_embeddings` para agrupar textos de índice
Atualizar `document_embeddings` se o agrupamento de mensagens for viável
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. **Aprimoramento do Gateway de API**
Adicionar endpoint de embedding em lote
Suportar array de textos no corpo da requisição
Módulo: `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
#### 7. **Aprimoramento da Ferramenta de Linha de Comando (CLI)**
Adicionar suporte para múltiplos textos ou entrada de arquivo
Adicionar parâmetro de tamanho do lote
Módulo: `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
#### 8. **Aprimoramento do SDK Python**
Adicionar método `embeddings_batch()` para `FlowInstance`
Adicionar método `embeddings_batch()` para `SocketFlowInstance`
Suportar APIs de texto único e em lote para usuários do SDK
Módulos:
`trustgraph-base/trustgraph/api/flow.py`
`trustgraph-base/trustgraph/api/socket_client.py`
### Modelos de Dados
#### 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)
```
Estrutura da resposta:
`vectors[i]` contém o conjunto de vetores para `texts[i]`
Cada conjunto de vetores é `list[list[float]]` (os modelos podem retornar vários vetores por texto)
Exemplo: 3 textos → `vectors` tem 3 entradas, cada uma contendo os embeddings desse 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
```
#### Endpoint de Incorporação do Gateway de API
Endpoint atualizado que suporta incorporação única ou em lote:
```
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, ...]]
]
}
```
### Detalhes de Implementação
#### Fase 1: Alterações no Esquema
**EmbeddingsRequest:**
```python
@dataclass
class EmbeddingsRequest:
texts: list[str] = field(default_factory=list)
```
**EmbeddingsResponse:**
```python
@dataclass
class EmbeddingsResponse:
error: Error | None = None
vectors: list[list[list[float]]] = field(default_factory=list)
```
**Atualizado 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: Atualização do Processador FastEmbed
**Atual (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]
```
**Atualizado:**
```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: Atualização do Serviço de Incorporação de Grafos
**Atual (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(...))
```
**Atualizado (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: Atualização do Serviço de Incorporação de Dados
**Atual (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(...))
```
**Atualizado (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: Melhoria da Ferramenta de Linha de Comando (CLI)
**CLI Atualizado:**
```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: Melhoria do SDK 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"]
```
**Exemplos de uso do 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")
```
## Considerações de Segurança
**Limites de Tamanho da Requisição**: Impor o tamanho máximo do lote para evitar o esgotamento de recursos.
**Tratamento de Tempo Limite (Timeout)**: Ajustar os tempos limite de acordo com o tamanho do lote.
**Limites de Memória**: Monitorar o uso de memória para grandes lotes.
**Validação de Entrada**: Validar todos os textos no lote antes do processamento.
## Considerações de Desempenho
### Melhorias Esperadas
**Taxa de Transferência (Throughput):**
Texto único: ~10-50 textos/segundo (dependendo do modelo)
Lote (tamanho 32): ~200-500 textos/segundo (melhora de 5 a 10 vezes)
**Latência por Texto:**
Texto único: 50-200ms por texto
Lote (tamanho 32): 5-20ms por texto (média)
**Melhorias Específicas do Serviço:**
| Serviço | Atual | Em Lote | Melhoria |
|---------|---------|---------|-------------|
| Incorporações de Grafos (50 entidades) | 5-10s | 0,5-1s | 5-10x |
| Incorporações de Linhas (100 textos) | 10-20s | 1-2s | 5-10x |
| Ingestão de Documentos (1000 fragmentos) | 100-200s | 10-30s | 5-10x |
### Parâmetros de Configuração
```python
# Recommended defaults
DEFAULT_BATCH_SIZE = 32
MAX_BATCH_SIZE = 128
BATCH_TIMEOUT_MULTIPLIER = 2.0
```
## Estratégia de Testes
### Testes Unitários
Incorporação de texto única (compatibilidade com versões anteriores)
Tratamento de lotes vazios
Imposição do tamanho máximo do lote
Tratamento de erros para falhas parciais do lote
### Testes de Integração
Incorporação de lote de ponta a ponta através do Pulsar
Processamento de lote do serviço de incorporação de grafos
Processamento de lote do serviço de incorporação de linhas
Endpoint de lote do gateway de API
### Testes de Desempenho
Comparação de benchmark de taxa de transferência única versus lote
Uso de memória sob vários tamanhos de lote
Análise da distribuição de latência
## Plano de Migração
Esta é uma versão que introduz alterações significativas. Todas as fases são implementadas juntas.
### Fase 1: Alterações no Esquema
Substituir `text: str` por `texts: list[str]` em EmbeddingsRequest
Alterar o tipo de `vectors` para `list[list[list[float]]]` em EmbeddingsResponse
### Fase 2: Atualizações do Processador
Atualizar a assinatura de `on_embeddings` nos processadores FastEmbed e Ollama
Processar o lote completo em uma única chamada de modelo
### Fase 3: Atualizações do Cliente
Atualizar `EmbeddingsClient.embed()` para aceitar `texts: list[str]`
### Fase 4: Atualizações do Chamador
Atualizar graph_embeddings para incorporar contextos de entidades em lote
Atualizar row_embeddings para incorporar textos de índice em lote
Atualizar document_embeddings para usar o novo esquema
Atualizar a ferramenta de linha de comando (CLI)
### Fase 5: Gateway de API
Atualizar o endpoint de incorporação para o novo esquema
### Fase 6: SDK Python
Atualizar a assinatura de `FlowInstance.embeddings()`
Atualizar a assinatura de `SocketFlowInstance.embeddings()`
## Perguntas Abertas
**Incorporação de Lotes Grandes em Streaming**: Devemos suportar o streaming de resultados para lotes muito grandes (>100 textos)?
**Limites Específicos do Provedor**: Como devemos lidar com provedores com tamanhos máximos de lote diferentes?
**Tratamento de Falhas Parciais**: Se um texto em um lote falhar, devemos falhar o lote inteiro ou retornar resultados parciais?
**Incorporação em Lote de Documentos**: Devemos incorporar em lote em várias mensagens de Chunk ou manter o processamento por mensagem?
## Referências
[Documentação do FastEmbed](https://github.com/qdrant/fastembed)
[API de Incorporação do Ollama](https://github.com/ollama/ollama)
[Implementação do Serviço de Incorporação](trustgraph-base/trustgraph/base/embeddings_service.py)
[Otimização de Desempenho do GraphRAG](graphrag-performance-optimization.md)

View file

@ -0,0 +1,269 @@
---
layout: default
title: "Armazenamento de Grafos de Conhecimento Centrados em Entidades no Cassandra"
parent: "Portuguese (Beta)"
---
# Armazenamento de Grafos de Conhecimento Centrados em Entidades no 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.
## Visão Geral
Este documento descreve um modelo de armazenamento para grafos de conhecimento no estilo RDF no Apache Cassandra. O modelo usa uma abordagem **centrada em entidades**, onde cada entidade conhece cada quádrupla em que participa e o papel que desempenha. Isso substitui uma abordagem tradicional de permutação SPO em várias tabelas por apenas duas tabelas.
## Contexto e Motivação
### A Abordagem Tradicional
Um armazenamento RDF padrão no Cassandra requer várias tabelas desnormalizadas para cobrir padrões de consulta — normalmente 6 ou mais tabelas representando diferentes permutações de Sujeito, Predicado, Objeto e Conjunto de Dados (SPOD). Cada quádrupla é escrita em todas as tabelas, resultando em uma amplificação de escrita significativa, sobrecarga operacional e complexidade do esquema.
Além disso, a resolução de rótulos (busca de nomes legíveis para humanos para entidades) requer consultas separadas de ida e volta, o que é particularmente caro em casos de uso de IA e GraphRAG, onde os rótulos são essenciais para o contexto do LLM.
### A Perspectiva Centrada na Entidade
Cada quádrupla `(D, S, P, O)` envolve até 4 entidades. Ao escrever uma linha para a participação de cada entidade na quádrupla, garantimos que **qualquer consulta com pelo menos um elemento conhecido atingirá uma chave de partição**. Isso cobre todos os 16 padrões de consulta com uma única tabela de dados.
Benefícios principais:
**2 tabelas** em vez de 7+
**4 gravações por quádrupla** em vez de 6+
**Resolução de rótulos gratuita** — os rótulos de uma entidade estão localizados com seus relacionamentos, aquecendo naturalmente o cache da aplicação
**Todos os 16 padrões de consulta** atendidos por leituras de partição única
**Operações mais simples** — uma única tabela de dados para ajustar, compactar e reparar
## Esquema
### Tabela 1: quads_by_entity
A tabela de dados primária. Cada entidade tem uma partição contendo todas as quádruplas em que participa. Nomeada para refletir o padrão de consulta (busca por entidade).
Saída do contrato (deve seguir exatamente o formato abaixo).
```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)
);
```
**Chave de partição**: `(collection, entity)` — restrita à coleção, uma partição por entidade.
**Justificativa para a ordem das colunas de agrupamento**:
1. **role** — a maioria das consultas começa com "onde esta entidade é um sujeito/objeto"
2. **p** — próximo filtro mais comum, "me dê todas as relações `knows`"
3. **otype** — permite filtrar por relações com valor URI versus relações com valor literal
4. **s, o, d** — colunas restantes para garantir a unicidade
5. **dtype, lang** — distingue literais com o mesmo valor, mas com metadados de tipo diferentes (por exemplo, `"thing"` vs `"thing"@en` vs `"thing"^^xsd:string`)
### Tabela 2: quads_by_collection
Suporta consultas e exclusões no nível da coleção. Fornece um manifesto de todos os quads pertencentes a uma coleção. Nomeado para refletir o padrão de consulta (pesquisa por coleção).
```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 primeiro por conjunto de dados, permitindo a exclusão em nível de coleção ou de conjunto de dados. As colunas `otype`, `dtype` e `lang` estão incluídas na chave de agrupamento para distinguir literais com o mesmo valor, mas com metadados de tipo diferentes — em RDF, `"thing"`, `"thing"@en` e `"thing"^^xsd:string` são valores semanticamente distintos.
## Caminho de Escrita
Para cada quádruplo de entrada `(D, S, P, O)` dentro de uma coleção `C`, escreva **4 linhas** em `quads_by_entity` e **1 linha** em `quads_by_collection`.
### Exemplo
Dado o quádruplo na coleção `tenant1`:
```
Dataset: https://example.org/graph1
Subject: https://example.org/Alice
Predicate: https://example.org/knows
Object: https://example.org/Bob
```
Escreva 4 linhas para `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 |
Escreva 1 linha para `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 | | |
### Exemplo Literal
Para uma tripla de rótulo:
```
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)
```
O `otype` é `'L'`, `dtype` é `'xsd:string'`, e `lang` é `'en'`. O valor literal `"Alice Smith"` é armazenado em `o`. Apenas 3 linhas são necessárias em `quads_by_entity` — nenhuma linha é escrita para o literal como entidade, pois os literais não são entidades consultáveis independentes.
## Padrões de Consulta
### Todos os 16 Padrões DSPO
Na tabela abaixo, "Prefixo perfeito" significa que a consulta usa um prefixo contíguo das colunas de agrupamento. "Leitura de partição + filtro" significa que o Cassandra lê uma fatia de uma partição e filtra na memória — ainda eficiente, mas não uma correspondência de prefixo pura.
| # | Conhecido | Consulta de entidade | Prefixo de agrupamento | Eficiência |
|---|---|---|---|---|
| 1 | D,S,P,O | entidade=S, role='S', p=P | Correspondência completa | Prefixo perfeito |
| 2 | D,S,P,? | entidade=S, role='S', p=P | Filtro em D | Leitura de partição + filtro |
| 3 | D,S,?,O | entidade=S, role='S' | Filtro em D, O | Leitura de partição + filtro |
| 4 | D,?,P,O | entidade=O, role='O', p=P | Filtro em D | Leitura de partição + filtro |
| 5 | ?,S,P,O | entidade=S, role='S', p=P | Filtro em O | Leitura de partição + filtro |
| 6 | D,S,?,? | entidade=S, role='S' | Filtro em D | Leitura de partição + filtro |
| 7 | D,?,P,? | entidade=P, role='P' | Filtro em D | Leitura de partição + filtro |
| 8 | D,?,?,O | entidade=O, role='O' | Filtro em D | Leitura de partição + filtro |
| 9 | ?,S,P,? | entidade=S, role='S', p=P | — | **Prefixo perfeito** |
| 10 | ?,S,?,O | entidade=S, role='S' | Filtro em O | Leitura de partição + filtro |
| 11 | ?,?,P,O | entidade=O, role='O', p=P | — | **Prefixo perfeito** |
| 12 | D,?,?,? | entidade=D, role='G' | — | **Prefixo perfeito** |
| 13 | ?,S,?,? | entidade=S, role='S' | — | **Prefixo perfeito** |
| 14 | ?,?,P,? | entidade=P, role='P' | — | **Prefixo perfeito** |
| 15 | ?,?,?,O | entidade=O, role='O' | — | **Prefixo perfeito** |
| 16 | ?,?,?,? | — | Leitura completa | Apenas exploração |
**Resultado chave**: 7 dos 15 padrões não triviais são correspondências perfeitas de prefixo de agrupamento. Os 8 restantes são leituras de partição única com filtragem dentro da partição. Cada consulta com pelo menos um elemento conhecido atinge uma chave de partição.
O padrão 16 (?,?,?,?) não ocorre na prática, pois a coleção é sempre especificada, reduzindo-o ao padrão 12.
### Exemplos Comuns de Consulta
**Tudo sobre uma entidade:**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice';
```
**Todos os relacionamentos de saída para uma entidade:**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S';
```
**Predicado específico para uma entidade:**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S' AND p = 'https://example.org/knows';
```
**Rótulo para uma entidade (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';
```
Em seguida, filtre por aplicação no lado do cliente, se necessário, usando `lang = 'en'`.
**Apenas relacionamentos com valores URI (links de entidade para entidade):**
```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';
```
**Pesquisa reversa — o que aponta para esta entidade:**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Bob'
AND role = 'O';
```
## Resolução de Rótulos e Aquecimento do Cache
Uma das vantagens mais significativas do modelo centrado em entidades é que **a resolução de rótulos se torna um efeito colateral gratuito**.
No modelo tradicional de várias tabelas, buscar rótulos requer consultas separadas: recuperar triplas, identificar URIs de entidades nos resultados e, em seguida, buscar `rdfs:label` para cada uma. Esse padrão N+1 é caro.
No modelo centrado em entidades, consultar uma entidade retorna **todos** os seus quadros — incluindo seus rótulos, tipos e outras propriedades. Quando o aplicativo armazena em cache os resultados das consultas, os rótulos são pré-aquecidos antes que qualquer coisa os solicite.
Dois regimes de uso confirmam que isso funciona bem na prática:
**Consultas voltadas para o usuário**: conjuntos de resultados naturalmente pequenos, rótulos essenciais. As leituras de entidades pré-aquecem o cache.
**Consultas de IA/em lote**: conjuntos de resultados grandes com limites rígidos. Os rótulos são desnecessários ou necessários apenas para um subconjunto selecionado de entidades que já estão no cache.
A preocupação teórica de resolver rótulos para conjuntos de resultados enormes (por exemplo, 30.000 entidades) é atenuada pela observação prática de que nenhum usuário humano ou de IA processa tantos rótulos de forma útil. Os limites de consulta no nível da aplicação garantem que a pressão do cache permaneça gerenciável.
## Partições Largas e Reificação
A reificação (declarações RDF-star sobre declarações) cria entidades de hub — por exemplo, um documento de origem que suporta milhares de fatos extraídos. Isso pode produzir partições largas.
Fatores atenuantes:
**Limites de consulta no nível da aplicação**: todas as consultas do GraphRAG e voltadas para o usuário impõem limites rígidos, portanto, as partições largas nunca são totalmente examinadas no caminho de leitura quente.
**O Cassandra lida com leituras parciais de forma eficiente**: uma varredura de coluna de agrupamento com uma parada antecipada é rápida, mesmo em partições grandes.
**Exclusão de coleções** (a única operação que pode percorrer partições completas) é um processo de segundo plano aceitável.
## Exclusão de Coleções
Acionada por chamada de API, é executada em segundo plano (consistência eventual).
1. Leia `quads_by_collection` para a coleção de destino para obter todos os quadros.
2. Extraia entidades exclusivas das quadros (valores s, p, o, d).
3. Para cada entidade exclusiva, exclua a partição de `quads_by_entity`.
4. Exclua as linhas de `quads_by_collection`.
A tabela `quads_by_collection` fornece o índice necessário para localizar todas as partições de entidade sem uma varredura completa da tabela. As exclusões em nível de partição são eficientes porque `(collection, entity)` é a chave da partição.
## Caminho de Migração do Modelo de Múltiplas Tabelas
O modelo centrado em entidades pode coexistir com o modelo de múltiplas tabelas existente durante a migração:
1. Implante as tabelas `quads_by_entity` e `quads_by_collection` junto com as tabelas existentes.
2. Escreva em duplicata novas quadros para ambas as tabelas antigas e novas.
3. Preencha os dados existentes nas novas tabelas.
4. Migre os caminhos de leitura um padrão de consulta por vez.
5. Desative as tabelas antigas assim que todos os caminhos de leitura forem migrados.
## Resumo
| Aspecto | Tradicional (6 tabelas) | Centrado em entidade (2 tabelas) |
|---|---|---|
| Tabelas | 7+ | 2 |
| Escritas por quadro | 6+ | 5 (4 dados + 1 manifesto) |
| Resolução de rótulos | Consultas separadas | Gratuito via aquecimento do cache |
| Padrões de consulta | 16 em 6 tabelas | 16 em 1 tabela |
| Complexidade do esquema | Alta | Baixa |
| Sobrecarga operacional | 6 tabelas para ajustar/reparar | 1 tabela de dados |
| Suporte de reificação | Complexidade adicional | Adequação natural |
| Filtragem de tipo de objeto | Não disponível | Nativo (via agrupamento de otype) |
Saída do contrato (deve seguir exatamente o formato abaixo).

View file

@ -0,0 +1,228 @@
---
layout: default
title: "Especificação Técnica da Interface de Linha de Comando (CLI) para Explicabilidade"
parent: "Portuguese (Beta)"
---
# Especificação Técnica da Interface de Linha de Comando (CLI) para Explicabilidade
> **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.
## Status
Rascunho
## Visão Geral
Esta especificação descreve ferramentas de linha de comando para depurar e explorar dados de explicabilidade no TrustGraph. Essas ferramentas permitem que os usuários rastreiem como as respostas foram derivadas e depurem a cadeia de origem desde as arestas até os documentos de origem.
Três ferramentas de linha de comando:
1. **`tg-show-document-hierarchy`** - Mostrar a hierarquia de documento → página → trecho → aresta
2. **`tg-list-explain-traces`** - Listar todas as sessões GraphRAG com perguntas
3. **`tg-show-explain-trace`** - Mostrar o rastreamento completo de explicabilidade para uma sessão
## Objetivos
**Depuração**: Permitir que os desenvolvedores inspecionem os resultados do processamento de documentos
**Auditabilidade**: Rastrear qualquer fato extraído de volta ao seu documento de origem
**Transparência**: Mostrar exatamente como o GraphRAG derivou uma resposta
**Usabilidade**: Interface de linha de comando simples com configurações padrão sensatas
## Contexto
O TrustGraph possui dois sistemas de rastreabilidade:
1. **Rastreabilidade em tempo de extração** (veja `extraction-time-provenance.md`): Registra os relacionamentos de documento → página → trecho → aresta durante a ingestão. Armazenado em um grafo chamado `urn:graph:source` usando `prov:wasDerivedFrom`.
2. **Explicabilidade em tempo de consulta** (veja `query-time-explainability.md`): Registra a cadeia de pergunta → exploração → foco → síntese durante as consultas GraphRAG. Armazenado em um grafo chamado `urn:graph:retrieval`.
Limitações atuais:
Não há uma maneira fácil de visualizar a hierarquia de documentos após o processamento
É necessário consultar manualmente as triplas para ver os dados de explicabilidade
Não há uma visão consolidada de uma sessão GraphRAG
## Design Técnico
### Ferramenta 1: tg-show-document-hierarchy
**Propósito**: Dado um ID de documento, percorrer e exibir todas as 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 | Descrição |
|-----|-------------|
| `document_id` | URI do documento (posicional) |
| `-u/--api-url` | URL do gateway (padrão: `$TRUSTGRAPH_URL`) |
| `-t/--token` | Token de autenticação (padrão: `$TRUSTGRAPH_TOKEN`) |
| `-U/--user` | ID do usuário (padrão: `trustgraph`) |
| `-C/--collection` | Coleção (padrão: `default`) |
| `--show-content` | Incluir conteúdo do blob/documento |
| `--max-content` | Máximo de caracteres por blob (padrão: 200) |
| `--format` | Saída: `tree` (padrão), `json` |
**Implementação**:
1. Consultar triplas: `?child prov:wasDerivedFrom <document_id>` em `urn:graph:source`
2. Consultar recursivamente os filhos de cada resultado
3. Construir estrutura de árvore: Documento → Páginas → Blocos
4. Se `--show-content`, buscar conteúdo da API do bibliotecário
5. Exibir como árvore indentada ou JSON
**Exemplo de Saída**:
```
Document: urn:trustgraph:doc:abc123
Title: "Sample PDF"
Type: application/pdf
└── Page 1: urn:trustgraph:doc:abc123/p1
├── Chunk 0: urn:trustgraph:doc:abc123/p1/c0
│ Content: "The quick brown fox..." [truncated]
└── Chunk 1: urn:trustgraph:doc:abc123/p1/c1
Content: "Machine learning is..." [truncated]
```
### Ferramenta 2: tg-list-explain-traces
**Propósito**: Listar todas as sessões (perguntas) do GraphRAG em uma coleção.
**Uso**:
```bash
tg-list-explain-traces
tg-list-explain-traces --limit 20 --format json
```
**Argumentos**:
| Arg | Descrição |
|-----|-------------|
| `-u/--api-url` | URL do gateway |
| `-t/--token` | Token de autenticação |
| `-U/--user` | ID do usuário |
| `-C/--collection` | Coleção |
| `--limit` | Resultados máximos (padrão: 50) |
| `--format` | Saída: `table` (padrão), `json` |
**Implementação**:
1. Consulta: `?session tg:query ?text` em `urn:graph:retrieval`
2. Consulta de carimbos de data/hora: `?session prov:startedAtTime ?time`
3. Exibir como tabela
**Exemplo de Saída**:
```
Session ID | Question | Time
----------------------------------------------|--------------------------------|---------------------
urn:trustgraph:question:abc123 | What was the War on Terror? | 2024-01-15 10:30:00
urn:trustgraph:question:def456 | Who founded OpenAI? | 2024-01-15 09:15:00
```
### Ferramenta 3: tg-show-explain-trace
**Propósito**: Mostrar a cascata completa de explicabilidade para uma sessão 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 | Descrição |
|-----|-------------|
| `question_id` | URI da pergunta (posicional) |
| `-u/--api-url` | URL do gateway |
| `-t/--token` | Token de autenticação |
| `-U/--user` | ID do usuário |
| `-C/--collection` | Coleção |
| `--max-answer` | Número máximo de caracteres para a resposta (padrão: 500) |
| `--show-provenance` | Rastrear arestas para documentos de origem |
| `--format` | Saída: `text` (padrão), `json` |
**Implementação**:
1. Obter o texto da pergunta do predicado `tg:query`
2. Encontrar a exploração: `?exp prov:wasGeneratedBy <question_id>`
3. Encontrar o foco: `?focus prov:wasDerivedFrom <exploration_id>`
4. Obter as arestas selecionadas: `<focus_id> tg:selectedEdge ?edge`
5. Para cada aresta, obter `tg:edge` (tripla entre aspas) e `tg:reasoning`
6. Encontrar a síntese: `?synth prov:wasDerivedFrom <focus_id>`
7. Obter a resposta de `tg:document` através do bibliotecário
8. Se `--show-provenance`, rastrear as arestas para os documentos de origem
**Exemplo de Saída**:
```
=== GraphRAG Session: urn:trustgraph:question:abc123 ===
Question: What was the War on Terror?
Time: 2024-01-15 10:30:00
--- Exploration ---
Retrieved 50 edges from knowledge graph
--- Focus (Edge Selection) ---
Selected 12 edges:
1. (War on Terror, definition, "A military campaign...")
Reasoning: Directly defines the subject of the query
Source: chunk → page 2 → "Beyond the Vigilant State"
2. (Guantanamo Bay, part_of, War on Terror)
Reasoning: Shows key component of the campaign
--- Synthesis ---
Answer:
The War on Terror was a military campaign initiated...
[truncated at 500 chars]
```
## Arquivos a Criar
| Arquivo | Propósito |
|------|---------|
| `trustgraph-cli/trustgraph/cli/show_document_hierarchy.py` | Ferramenta 1 |
| `trustgraph-cli/trustgraph/cli/list_explain_traces.py` | Ferramenta 2 |
| `trustgraph-cli/trustgraph/cli/show_explain_trace.py` | Ferramenta 3 |
## Arquivos a Modificar
| Arquivo | Alteração |
|------|--------|
| `trustgraph-cli/setup.py` | Adicionar entradas de console_scripts |
## Notas de Implementação
1. **Segurança do conteúdo binário**: Tente decodificar UTF-8; se falhar, mostre `[Binary: {size} bytes]`
2. **Truncamento**: Respeite `--max-content`/`--max-answer` com indicador `[truncated]`
3. **Triplas entre aspas**: Analise o formato RDF-star a partir do predicado `tg:edge`
4. **Padrões**: Siga os padrões de CLI existentes a partir de `query_graph.py`
## Considerações de Segurança
Todas as consultas respeitam os limites de usuário/coleção
Autenticação por token suportada via `--token` ou `$TRUSTGRAPH_TOKEN`
## Estratégia de Teste
Verificação manual com dados de amostra:
```bash
# Load a test document
tg-load-pdf -f test.pdf -c test-collection
# Verify hierarchy
tg-show-document-hierarchy "urn:trustgraph:doc:test"
# Run a GraphRAG query with explainability
tg-invoke-graph-rag --explainable -q "Test question"
# List and inspect traces
tg-list-explain-traces
tg-show-explain-trace "urn:trustgraph:question:xxx"
```
## Referências
Explicabilidade em tempo de consulta: `docs/tech-specs/query-time-explainability.md`
Proveniência em tempo de extração: `docs/tech-specs/extraction-time-provenance.md`
Exemplo de CLI existente: `trustgraph-cli/trustgraph/cli/invoke_graph_rag.py`

View file

@ -0,0 +1,355 @@
---
layout: default
title: "Fluxos de Extração"
parent: "Portuguese (Beta)"
---
# Fluxos de Extração
> **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 descreve como os dados fluem através do pipeline de extração do TrustGraph, desde o envio do documento até ao armazenamento nos repositórios de conhecimento.
## Visão Geral
```
┌──────────┐ ┌─────────────┐ ┌─────────┐ ┌────────────────────┐
│ Librarian│────▶│ PDF Decoder │────▶│ Chunker │────▶│ Knowledge │
│ │ │ (PDF only) │ │ │ │ Extraction │
│ │────────────────────────▶│ │ │ │
└──────────┘ └─────────────┘ └─────────┘ └────────────────────┘
│ │
│ ├──▶ Triples
│ ├──▶ Entity Contexts
│ └──▶ Rows
└──▶ Document Embeddings
```
## Armazenamento de Conteúdo
### Armazenamento de Blobs (S3/Minio)
O conteúdo do documento é armazenado em armazenamento de blobs compatível com S3:
Formato do caminho: `doc/{object_id}`, onde object_id é um UUID
Todos os tipos de documentos armazenados aqui: documentos de origem, páginas, trechos
### Armazenamento de Metadados (Cassandra)
Os metadados do documento armazenados no Cassandra incluem:
ID do documento, título, tipo (MIME)
Referência ao armazenamento de blobs `object_id`
Referência `parent_id` para documentos filhos (páginas, trechos)
`document_type`: "source", "page", "chunk", "answer"
### Limiar de Incorporação vs. Streaming
A transmissão de conteúdo usa uma estratégia baseada no tamanho:
**< 2MB**: O conteúdo é incluído inline na mensagem (codificado em base64)
**≥ 2MB**: Apenas `document_id` é enviado; o processador busca via API do bibliotecário
## Etapa 1: Envio de Documento (Bibliotecário)
### Ponto de Entrada
Os documentos entram no sistema através da operação `add-document` do bibliotecário:
1. O conteúdo é carregado no armazenamento de blobs
2. Um registro de metadados é criado no Cassandra
3. Retorna o ID do documento
### Disparando a Extração
A operação `add-processing` dispara a extração:
Especifica `document_id`, `flow` (ID do pipeline), `collection` (loja de destino)
A operação `load_document()` do bibliotecário busca o conteúdo e o publica na fila de entrada do fluxo
### 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)
```
**Roteamento**: Baseado no campo `kind`:
`application/pdf` → fila `document-load` → Decodificador PDF
`text/plain` → fila `text-load` → Fragmentador
## Etapa 2: Decodificador PDF
Converte documentos PDF em páginas de texto.
### Processo
1. Buscar conteúdo (inline `data` ou via `document_id` do bibliotecário)
2. Extrair páginas usando PyPDF
3. Para cada página:
Salvar como documento filho no bibliotecário (`{doc_id}/p{page_num}`)
Emitir triplas de procedência (página derivada do documento)
Enviar para o 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: Divisor em Blocos
Divide o texto em blocos de tamanho configurado.
### Parâmetros (configuráveis no fluxo)
`chunk_size`: Tamanho do bloco alvo em caracteres (padrão: 2000)
`chunk_overlap`: Sobreposição entre blocos (padrão: 100)
### Processo
1. Buscar o conteúdo do texto (inline ou via bibliotecário)
2. Dividir usando o divisor de caracteres recursivo
3. Para cada bloco:
Salvar como documento filho no bibliotecário (`{parent_id}/c{index}`)
Emitir triplas de procedência (bloco derivado da página/documento)
Encaminhar para os processadores de extração
### Esquema: Bloco
```
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")
```
### Hierarquia de Identificação de Documentos
Documentos filhos codificam sua linhagem no ID:
Fonte: `doc123`
Página: `doc123/p5`
Trecho da página: `doc123/p5/c2`
Trecho de texto: `doc123/c2`
## Etapa 4: Extração de Conhecimento
Múltiplos padrões de extração disponíveis, selecionados pela configuração do fluxo.
### Padrão A: Basic GraphRAG
Dois processadores paralelos:
**kg-extract-definitions**
Entrada: Trecho
Saída: Triplas (definições de entidades), Contextos de Entidade
Extrai: rótulos de entidades, definições
**kg-extract-relationships**
Entrada: Trecho
Saída: Triplas (relacionamentos), Contextos de Entidade
Extrai: relações sujeito-predicado-objeto
### Padrão B: Orientado a Ontologia (kg-extract-ontology)
Entrada: Trecho
Saída: Triplas, Contextos de Entidade
Utiliza uma ontologia configurada para guiar a extração
### Padrão C: Baseado em Agente (kg-extract-agent)
Entrada: Trecho
Saída: Triplas, Contextos de Entidade
Utiliza um framework de agente para a extração
### Padrão D: Extração de Linhas (kg-extract-rows)
Entrada: Trecho
Saída: Linhas (dados estruturados, não triplas)
Utiliza uma definição de esquema para extrair registros estruturados
### Esquema: Triplas
```
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: Linhas
```
Rows
├── metadata: Metadata
├── row_schema: RowSchema
│ ├── name: str
│ ├── description: str
│ └── fields: list[Field]
└── rows: list[dict[str, str]] # Extracted records
```
## Etapa 5: Geração de Embeddings
### Embeddings de Grafos
Converte contextos de entidades em embeddings vetoriais.
**Processo:**
1. Receber EntityContexts
2. Chamar o serviço de embeddings com o texto do contexto
3. Output GraphEmbeddings (mapeamento de entidade para vetor)
**Esquema: GraphEmbeddings**
```
GraphEmbeddings
├── metadata: Metadata
└── entities: list[EntityEmbeddings]
└── EntityEmbeddings
├── entity: Term # Entity identifier
├── vector: list[float] # Embedding vector
└── chunk_id: str # Source chunk (provenance)
```
### Incorporações de Documentos
Converte texto em partes diretamente em incorporações vetoriais.
**Processo:**
1. Receber Parte (Chunk)
2. Chamar o serviço de incorporação com o texto da parte
3. Saída: Incorporações de Documento (DocumentEmbeddings)
**Esquema: Incorporações de Documento (DocumentEmbeddings)**
```
DocumentEmbeddings
├── metadata: Metadata
└── chunks: list[ChunkEmbeddings]
└── ChunkEmbeddings
├── chunk_id: str # Chunk identifier
└── vector: list[float] # Embedding vector
```
### Incorporações de Linhas
Converte campos de índice de linha em incorporações vetoriais.
**Processo:**
1. Receber Linhas
2. Incorporar campos de índice configurados
3. Saída para o armazenamento vetorial de linhas
## Fase 6: Armazenamento
### Armazenamento Triplo
Recebe: Triplas
Armazenamento: Cassandra (tabelas centradas em entidades)
Grafos nomeados separam o conhecimento central da procedência:
`""` (padrão): Fatos de conhecimento central
`urn:graph:source`: Procedência de extração
`urn:graph:retrieval`: Explicabilidade em tempo de consulta
### Armazenamento Vetorial (Incorporações de Grafos)
Recebe: Incorporações de Grafos
Armazenamento: Qdrant, Milvus ou Pinecone
Indexado por: IRI da entidade
Metadados: chunk_id para procedência
### Armazenamento Vetorial (Incorporações de Documentos)
Recebe: Incorporações de Documentos
Armazenamento: Qdrant, Milvus ou Pinecone
Indexado por: chunk_id
### Armazenamento de Linhas
Recebe: Linhas
Armazenamento: Cassandra
Estrutura de tabela orientada por esquema
### Armazenamento Vetorial de Linhas
Recebe: Incorporações de linhas
Armazenamento: Banco de dados vetorial
Indexado por: campos de índice de linha
## Análise de Campos de Metadados
### Campos Ativamente Utilizados
| Campo | Uso |
|-------|-------|
| `metadata.id` | Identificador de documento/fragmento, registro, procedência |
| `metadata.user` | Multilocação, roteamento de armazenamento |
| `metadata.collection` | Seleção de coleção de destino |
| `document_id` | Referência do bibliotecário, vinculação de procedência |
| `chunk_id` | Rastreamento de procedência através do pipeline |
<<<<<<< HEAD
### Campos Potencialmente Redundantes
| Campo | Status |
|-------|--------|
| `metadata.metadata` | Definido como `[]` por todos os extratores; metadados de nível de documento agora gerenciados pelo bibliotecário no momento do envio |
=======
### Campos Removidos
| Campo | Status |
|-------|--------|
| `metadata.metadata` | Removido da classe `Metadata`. Triplas de metadados de nível de documento agora são emitidas diretamente pelo bibliotecário para o armazenamento de triplas no momento do envio, e não são transmitidas através do pipeline de extração. |
>>>>>>> e3bcbf73 (The metadata field (list of triples) in the pipeline Metadata class)
### Padrão de Campos de Bytes
Todos os campos de conteúdo (`data`, `text`, `chunk`) são `bytes`, mas são imediatamente decodificados para strings UTF-8 por todos os processadores. Nenhum processador usa bytes brutos.
## Configuração do Fluxo
Os fluxos são definidos externamente e fornecidos ao bibliotecário através do serviço de configuração. Cada fluxo especifica:
Filas de entrada (`text-load`, `document-load`)
Cadeia de processadores
Parâmetros (tamanho do fragmento, método de extração, etc.)
Padrões de fluxo de exemplo:
`pdf-graphrag`: PDF → Decodificador → Fragmentador → Definições + Relacionamentos → Incorporações
`text-graphrag`: Texto → Fragmentador → Definições + Relacionamentos → Incorporações
`pdf-ontology`: PDF → Decodificador → Fragmentador → Extração de Ontologia → Incorporações
`text-rows`: Texto → Fragmentador → Extração de Linhas → Armazenamento de Linhas

View file

@ -0,0 +1,247 @@
---
layout: default
title: "Proveniência de Extração: Modelo de Subgrafo"
parent: "Portuguese (Beta)"
---
# Proveniência de Extração: 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
A proveniência em tempo de extração atualmente gera uma reificação completa para cada
<<<<<<< HEAD
tripla extraída: um `stmt_uri`, `activity_uri` e metadados PROV-O associados para cada
fato de conhecimento. O processamento de um bloco que gera 20 relacionamentos produz aproximadamente 220 triplas de proveniência, além de
aproximadamente 20 triplas de conhecimento — uma sobrecarga de aproximadamente 10:1.
Isso é caro (armazenamento, indexação, transmissão) e semanticamente
impreciso. Cada bloco é processado por uma única chamada de LLM que produz
todas as suas triplas em uma única transação. O modelo atual, baseado em tripla,
=======
tripla extraída: um `stmt_uri`, `activity_uri` e metadados PROV-O associados para
cada fato de conhecimento. O processamento de um bloco que gera 20 relacionamentos produz
aproximadamente 220 triplas de proveniência, além das aproximadamente 20 triplas de
conhecimento — uma sobrecarga de aproximadamente 10:1.
Isso é caro (armazenamento, indexação, transmissão) e semanticamente
impreciso. Cada bloco é processado por uma única chamada de LLM que produz
todas as suas triplas em uma única transação. O modelo atual, por tripla,
>>>>>>> 82edf2d (New md files from RunPod)
obscure isso, criando a ilusão de 20 eventos de extração independentes.
Além disso, dois dos quatro processadores de extração (kg-extract-ontology,
<<<<<<< HEAD
kg-extract-agent) não possuem proveniência, deixando lacunas no registro de auditoria.
## Solução
Substituir a reificação por tripla por um **modelo de subgrafo**: um registro de proveniência
por extração de bloco, compartilhado entre todas as triplas produzidas a partir desse
bloco.
=======
kg-extract-agent) não possuem proveniência, deixando lacunas no registro de
auditoria.
## Solução
Substituir a reificação por tripla por um **modelo de subgrafo**: um registro de
proveniência por extração de bloco, compartilhado entre todas as triplas produzidas
a partir desse bloco.
>>>>>>> 82edf2d (New md files from RunPod)
### Mudança de Terminologia
| Antigo | Novo |
|-----|-----|
| `stmt_uri` (`https://trustgraph.ai/stmt/{uuid}`) | `subgraph_uri` (`https://trustgraph.ai/subgraph/{uuid}`) |
| `statement_uri()` | `subgraph_uri()` |
| `tg:reifies` (1:1, identidade) | `tg:contains` (1:muitos, contenção) |
### Estrutura Alvo
Todas as triplas de proveniência devem ser inseridas no grafo nomeado `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}" .
```
### Comparação de Volume
Para um conjunto de dados que produz N triplas extraídas:
| | Antigo (por tripla) | Novo (subgrafo) |
|---|---|---|
| `tg:contains` / `tg:reifies` | N | N |
| Triplas de atividade | ~9 x N | ~9 |
| Triplas de agente | 2 x N | 2 |
| Metadados de declaração/subgrafo | 2 x N | 2 |
| **Total de triplas de rastreabilidade** | **~13N** | **N + 13** |
| **Exemplo (N=20)** | **~260** | **33** |
## Escopo
### Processadores a serem Atualizados (rastreabilidade existente, por tripla)
**kg-extract-definitions**
(`trustgraph-flow/trustgraph/extract/kg/definitions/extract.py`)
Atualmente, chama `statement_uri()` + `triple_provenance_triples()` dentro
do loop por definição.
Alterações:
Mover a criação de `subgraph_uri()` e `activity_uri()` antes do loop
Coletar triplas `tg:contains` dentro do loop
Emitir bloco compartilhado de atividade/agente/derivação uma vez após o loop
**kg-extract-relationships**
(`trustgraph-flow/trustgraph/extract/kg/relationships/extract.py`)
Mesmo padrão que definições. As mesmas alterações.
### Processadores a serem Adicionados para Rastreabilidade (atualmente ausente)
**kg-extract-ontology**
(`trustgraph-flow/trustgraph/extract/kg/ontology/extract.py`)
Atualmente, emite triplas sem rastreabilidade. Adicionar rastreabilidade de subgrafo
usando o mesmo padrão: um subgrafo por conjunto de dados, `tg:contains` para cada
tripla extraída.
**kg-extract-agent**
(`trustgraph-flow/trustgraph/extract/kg/agent/extract.py`)
Atualmente, emite triplas sem rastreabilidade. Adicionar rastreabilidade de subgrafo
usando o mesmo padrão.
### Alterações na Biblioteca Compartilhada de Rastreabilidade
**`trustgraph-base/trustgraph/provenance/triples.py`**
Substituir `triple_provenance_triples()` por `subgraph_provenance_triples()`
Nova função aceita uma lista de triplas extraídas em vez de uma única
Gera um `tg:contains` por tripla, bloco compartilhado de atividade/agente
Remover `triple_provenance_triples()` antigo
**`trustgraph-base/trustgraph/provenance/uris.py`**
Substituir `statement_uri()` por `subgraph_uri()`
**`trustgraph-base/trustgraph/provenance/namespaces.py`**
Substituir `TG_REIFIES` por `TG_CONTAINS`
<<<<<<< HEAD
### Não no Escopo
=======
### Não está no Escopo
>>>>>>> 82edf2d (New md files from RunPod)
**kg-extract-topics**: processador de estilo antigo, não usado atualmente em
fluxos padrão
**kg-extract-rows**: produz linhas, não triplas, modelo de rastreabilidade
diferente
**Rastreabilidade em tempo de consulta** (`urn:graph:retrieval`): questão separada,
já usa um padrão diferente (pergunta/exploração/foco/síntese)
**Rastreabilidade de documento/página/conjunto de dados** (decodificador PDF, divisor): já usa
`derived_entity_triples()` que é por entidade, não por tripla — não há
problema de redundância
## Notas de Implementação
### Reestruturação do Loop do Processador
Antes (por tripla, em relacionamentos):
```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))
```
Apó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))
```
<<<<<<< HEAD
### Nova Assinatura de Auxílio
=======
### Nova Assinatura de Ajuda
>>>>>>> 82edf2d (New md files from RunPod)
```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
"""
```
### Mudança Significativa
<<<<<<< HEAD
Esta é uma mudança significativa no modelo de rastreabilidade. A rastreabilidade não
=======
Esta é uma mudança significativa no modelo de rastreabilidade. A rastreabilidade ainda não
>>>>>>> 82edf2d (New md files from RunPod)
foi lançada, portanto, nenhuma migração é necessária. O código antigo `tg:reifies` /
`statement_uri` pode ser removido completamente.

View file

@ -0,0 +1,758 @@
---
layout: default
title: "Proveniência no Momento da Extração: Camada de Origem"
parent: "Portuguese (Beta)"
---
# Proveniência no Momento da Extração: Camada de Origem
> **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.
## Visão Geral
Este documento registra notas sobre a proveniência no momento da extração para trabalhos de especificação futuros. A proveniência no momento da extração registra a "camada de origem" - de onde os dados vieram originalmente, como foram extraídos e transformados.
Isso é diferente da proveniência no momento da consulta (veja `query-time-provenance.md`), que registra o raciocínio do agente.
## Declaração do Problema
### Implementação Atual
Atualmente, a proveniência funciona da seguinte forma:
Metadados do documento são armazenados como triplas RDF no grafo de conhecimento.
Um ID de documento associa metadados ao documento, de modo que o documento aparece como um nó no grafo.
Quando arestas (relacionamentos/fatos) são extraídas de documentos, um relacionamento `subjectOf` vincula a aresta extraída ao documento de origem.
### Problemas com a Abordagem Atual
<<<<<<< HEAD
1. **Carregamento repetitivo de metadados:** Os metadados do documento são agrupados e carregados repetidamente com cada lote de triplas extraídas daquele documento. Isso é um desperdício e redundante - os mesmos metadados viajam como carga com cada saída de extração.
=======
1. **Carregamento repetitivo de metadados:** Os metadados do documento são agrupados e carregados repetidamente com cada lote de triplas extraídas daquele documento. Isso é um desperdício e redundante - os mesmos metadados viajam como carga útil com cada saída de extração.
>>>>>>> 82edf2d (New md files from RunPod)
2. **Proveniência superficial:** O relacionamento `subjectOf` atual vincula apenas os fatos diretamente ao documento de nível superior. Não há visibilidade da cadeia de transformação - qual página o fato veio, qual trecho, qual método de extração foi usado.
### Estado Desejado
1. **Carregar metadados uma vez:** Os metadados do documento devem ser carregados uma vez e anexados ao nó do documento de nível superior, não repetidos com cada lote de triplas.
2. **DAG de proveniência rica:** Capture toda a cadeia de transformação desde o documento de origem, passando por todos os artefatos intermediários, até os fatos extraídos. Por exemplo, uma transformação de documento PDF:
```
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
→ ...
```
<<<<<<< HEAD
3. **Armazenamento unificado:** O grafo de proveniência é armazenado no mesmo grafo de conhecimento que o conhecimento extraído. Isso permite que a proveniência seja consultada da mesma forma que o conhecimento - seguindo as arestas de volta à cadeia de qualquer fato para sua localização de origem exata.
=======
3. **Armazenamento unificado:** O grafo de proveniência é armazenado no mesmo grafo de conhecimento que o conhecimento extraído. Isso permite que a proveniência seja consultada da mesma forma que o conhecimento - seguindo as arestas de volta à cadeia de qualquer fato até sua localização de origem exata.
>>>>>>> 82edf2d (New md files from RunPod)
4. **IDs estáveis:** Cada artefato intermediário (página, trecho) possui um ID estável como um nó no grafo.
5. **Vinculação pai-filho:** Documentos derivados são vinculados aos seus pais até o documento de origem de nível superior, usando tipos de relacionamento consistentes.
<<<<<<< HEAD
6. **Atribuição precisa de fatos:** O relacionamento `subjectOf` nas arestas extraídas aponta para o pai imediato (trecho), não para o documento de nível superior. A proveniência completa é recuperada percorrendo o DAG.
=======
6. **Atribuição precisa de fatos:** O relacionamento `subjectOf` nas arestas extraídas aponta para o pai imediato (trecho), e não para o documento de nível superior. A proveniência completa é recuperada percorrendo o DAG.
>>>>>>> 82edf2d (New md files from RunPod)
## Casos de Uso
### UC1: Atribuição de Fonte em Respostas GraphRAG
**Cenário:** Um usuário executa uma consulta GraphRAG e recebe uma resposta do agente.
**Fluxo:**
1. O usuário envia uma consulta para o agente GraphRAG.
2. O agente recupera fatos relevantes do grafo de conhecimento para formular uma resposta.
3. De acordo com a especificação de proveniência em tempo de consulta, o agente informa quais fatos contribuíram para a resposta.
4. Cada fato vincula-se ao seu trecho de origem através do grafo de proveniência.
5. Trechos vinculam-se a páginas, páginas vinculam-se a documentos de origem.
**Resultado da Experiência do Usuário:** A interface exibe a resposta do LLM juntamente com a atribuição da fonte. O usuário pode:
<<<<<<< HEAD
Ver quais fatos apoiaram a resposta.
=======
Ver quais fatos suportaram a resposta.
>>>>>>> 82edf2d (New md files from RunPod)
Acessar informações detalhadas de fatos → trechos → páginas → documentos.
Examinar os documentos de origem para verificar as alegações.
Entender exatamente onde em um documento (qual página, qual seção) um fato se originou.
**Valor:** Os usuários podem verificar as respostas geradas por IA em relação às fontes primárias, construindo confiança e permitindo a verificação de fatos.
### UC2: Depuração da Qualidade da Extração
Um fato parece incorreto. Rastreie de volta através do trecho → página → documento para ver o texto original. Foi uma extração ruim ou a fonte em si estava incorreta?
<<<<<<< HEAD
### UC3: Reextração Incremental
=======
### UC3: Re-extração Incremental
>>>>>>> 82edf2d (New md files from RunPod)
O documento de origem é atualizado. Quais trechos/fatos foram derivados dele? Invalide e regenere apenas esses, em vez de reprocessar tudo.
### UC4: Exclusão de Dados / Direito ao Esquecimento
Um documento de origem deve ser removido (GDPR, legal, etc.). Percorra o DAG para encontrar e remover todos os fatos derivados.
### UC5: Resolução de Conflitos
<<<<<<< HEAD
Dois fatos se contradizem. Rastreie ambos de volta às suas fontes para entender por que e decidir qual confiar (fonte mais autoritária, mais recente, etc.).
=======
Dois fatos se contradizem. Rastreie ambos de volta às suas fontes para entender o porquê e decidir qual confiar (fonte mais autoritária, mais recente, etc.).
>>>>>>> 82edf2d (New md files from RunPod)
### UC6: Ponderação da Autoridade da Fonte
Algumas fontes são mais autoritárias do que outras. Os fatos podem ser ponderados ou filtrados com base na autoridade/qualidade de seus documentos de origem.
### UC7: Comparação de Pipelines de Extração
Compare os resultados de diferentes métodos/versões de extração. Qual extrator produziu melhores fatos da mesma fonte?
## Pontos de Integração
### Bibliotecário
<<<<<<< HEAD
O componente bibliotecário já fornece armazenamento de documentos com IDs de documento exclusivos. O sistema de rastreabilidade se integra com essa infraestrutura existente.
=======
O componente bibliotecário já fornece armazenamento de documentos com IDs de documentos exclusivos. O sistema de rastreabilidade se integra a essa infraestrutura existente.
>>>>>>> 82edf2d (New md files from RunPod)
#### Capacidades Existentes (já implementadas)
**Vinculação de Documentos Pai-Filho:**
Campo `parent_id` em `DocumentMetadata` - vincula o documento filho ao documento pai
Campo `document_type` - valores: `"source"` (original) ou `"extracted"` (derivado)
API `add-child-document` - cria um documento filho com `document_type = "extracted"` automático
API `list-children` - recupera todos os filhos de um documento pai
Exclusão em cascata - a remoção de um pai exclui automaticamente todos os documentos filhos
**Identificação de Documentos:**
<<<<<<< HEAD
Os IDs de documento são especificados pelo cliente (não gerados automaticamente)
Documentos indexados por `(user, document_id)` composto no Cassandra
IDs de objeto (UUIDs) gerados internamente para armazenamento de blobs
=======
Os IDs dos documentos são especificados pelo cliente (não gerados automaticamente)
Documentos indexados por `(user, document_id)` composto no Cassandra
IDs de objetos (UUIDs) gerados internamente para armazenamento de blobs
>>>>>>> 82edf2d (New md files from RunPod)
**Suporte a Metadados:**
Campo `metadata: list[Triple]` - triplas RDF para metadados estruturados
`title`, `comments`, `tags` - metadados básicos do documento
`time` - carimbo de data/hora, `kind` - tipo MIME
**Arquitetura de Armazenamento:**
Metadados armazenados no Cassandra (espaço de chaves `librarian`, tabela `document`)
Conteúdo armazenado no armazenamento de blobs MinIO/S3 (bucket `library`)
Entrega inteligente de conteúdo: documentos < 2MB incorporados, documentos maiores transmitidos
#### Arquivos Chave
`trustgraph-flow/trustgraph/librarian/librarian.py` - Operações principais do bibliotecário
<<<<<<< HEAD
`trustgraph-flow/trustgraph/librarian/service.py` - Processador de serviço, carregamento de documento
=======
`trustgraph-flow/trustgraph/librarian/service.py` - Processador de serviço, carregamento de documentos
>>>>>>> 82edf2d (New md files from RunPod)
`trustgraph-flow/trustgraph/tables/library.py` - Armazenamento de tabela Cassandra
`trustgraph-base/trustgraph/schema/services/library.py` - Definições de esquema
#### Lacunas a Serem Abordadas
O bibliotecário tem os blocos de construção, mas atualmente:
1. A vinculação pai-filho é de um único nível - não há auxiliares de travessia de DAG de vários níveis
2. Não há vocabulário padrão de tipo de relacionamento (por exemplo, `derivedFrom`, `extractedFrom`)
3. Metadados de rastreabilidade (método de extração, confiança, posição do fragmento) não estão padronizados
<<<<<<< HEAD
4. Não há API de consulta para percorrer toda a cadeia de rastreabilidade de um fato até a fonte
=======
4. Não há API de consulta para percorrer toda a cadeia de rastreabilidade de um fato até a origem
>>>>>>> 82edf2d (New md files from RunPod)
## Design de Fluxo de Extremo a Extremo
Cada processador no pipeline segue um padrão consistente:
Recebe o ID do documento do upstream
Recupera o conteúdo do bibliotecário
Produz artefatos filhos
Para cada filho: salva no bibliotecário, emite uma aresta para o grafo, encaminha o ID para o downstream
### Fluxos de Processamento
Existem dois fluxos dependendo do tipo de documento:
#### Fluxo de Documento PDF
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Fluxo de Documentos de Texto
Documentos de texto ignoram o extrator de PDF e vão diretamente para o processador de fragmentos:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 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) │
└─────────────────────────────────────────────────────────────────────────┘
```
<<<<<<< HEAD
O grafo acíclico dirigido (DAG) resultante é um nível mais curto:
=======
O grafo acíclico direcionado (DAG) resultante é um nível mais curto:
>>>>>>> 82edf2d (New md files from RunPod)
```
PDF: Document → Pages → Chunks → Triples/Embeddings
Text: Document → Chunks → Triples/Embeddings
```
O design acomoda ambos porque o processador divide o conteúdo de forma genérica - ele usa qualquer ID de documento que recebe como pai, independentemente de ser um documento de origem ou uma página.
### Esquema de Metadados (PROV-O)
Os metadados de procedência utilizam a ontologia W3C PROV-O. Isso fornece um vocabulário padrão e permite a futura assinatura/autenticação dos resultados da extração.
#### Conceitos Principais do PROV-O
| Tipo PROV-O | Uso no TrustGraph |
|-------------|------------------|
| `prov:Entity` | Documento, Página, Trecho, Tripla, Incorporação |
| `prov:Activity` | Instâncias de operações de extração |
| `prov:Agent` | Componentes do TG (extrator de PDF, processador, etc.) com versões |
#### Relacionamentos do PROV-O
| Predicado | Significado | Exemplo |
|-----------|---------|---------|
<<<<<<< HEAD
| `prov:wasDerivedFrom` | Entidade derivada de outra entidade | Página foiDerivadaDe Documento |
| `prov:wasGeneratedBy` | Entidade gerada por uma atividade | Página foiGeradaPor AtividadeDeExtraçãoDePDF |
| `prov:used` | Atividade que usou uma entidade como entrada | AtividadeDeExtraçãoDePDF usou Documento |
| `prov:wasAssociatedWith` | Atividade realizada por um agente | AtividadeDeExtraçãoDePDF foiAssociadaA tg:ExtratorDePDF |
=======
| `prov:wasDerivedFrom` | Entidade derivada de outra entidade | Página derivadaDocumento |
| `prov:wasGeneratedBy` | Entidade gerada por uma atividade | Página geradaPor AtividadeDeExtraçãoDePDF |
| `prov:used` | Atividade que usou uma entidade como entrada | AtividadeDeExtraçãoDePDF usou Documento |
| `prov:wasAssociatedWith` | Atividade realizada por um agente | AtividadeDeExtraçãoDePDF associadaA tg:ExtratorDePDF |
>>>>>>> 82edf2d (New md files from RunPod)
#### Metadados em Cada Nível
**Documento de Origem (emitido pelo 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 pelo Extrator 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" .
```
**Bloco (emitido pelo Chunker):**
```
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 .
```
**Tripla (emitida pelo Extrator de Conhecimento):**
```
# 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> .
```
**Incorporação (armazenada em um armazenamento vetorial, não em um armazenamento triplo):**
As incorporações são armazenadas no armazenamento vetorial com metadados, e não como triplos RDF. Cada registro de incorporação contém:
| Campo | Descrição | Exemplo |
|-------|-------------|---------|
| vetor | O vetor de incorporação | [0.123, -0.456, ...] |
| entidade | URI do nó que a incorporação representa | `entity:JohnSmith` |
| chunk_id | Fragmento de origem (proveniência) | `chunk:123-1-1` |
| modelo | Modelo de incorporação usado | `text-embedding-ada-002` |
| component_version | Versão do incorporador TG | `1.0.0` |
<<<<<<< HEAD
O campo `entity` vincula a incorporação ao grafo de conhecimento (URI do nó). O campo `chunk_id` fornece a proveniência de volta ao fragmento de origem, permitindo a navegação ascendente no DAG até o documento original.
=======
O campo `entity` vincula a incorporação ao grafo de conhecimento (URI do nó). O campo `chunk_id` fornece a proveniência de volta ao fragmento de origem, permitindo a travessia ascendente do DAG até o documento original.
>>>>>>> 82edf2d (New md files from RunPod)
#### Extensões do Namespace TrustGraph
Predicados personalizados sob o namespace `tg:` para metadados específicos de extração:
| Predicado | Domínio | Descrição |
|-----------|--------|-------------|
| `tg:contains` | Subgrafo | Aponta para um triplo contido neste subgrafo de extração |
| `tg:pageCount` | Documento | Número total de páginas no documento de origem |
| `tg:mimeType` | Documento | Tipo MIME do documento de origem |
| `tg:pageNumber` | Página | Número da página no documento de origem |
| `tg:chunkIndex` | Fragmento | Índice do fragmento dentro do fragmento pai |
| `tg:charOffset` | Fragmento | Deslocamento de caractere no texto pai |
| `tg:charLength` | Fragmento | Comprimento do fragmento em caracteres |
| `tg:chunkSize` | Atividade | Tamanho do fragmento configurado |
| `tg:chunkOverlap` | Atividade | Sobreposição configurada entre fragmentos |
| `tg:componentVersion` | Atividade | Versão do componente TG |
| `tg:llmModel` | Atividade | LLM usado para extração |
| `tg:ontology` | Atividade | URI da ontologia usada para guiar a extração |
| `tg:embeddingModel` | Atividade | Modelo usado para incorporações |
| `tg:sourceText` | Declaração | Texto exato do qual um triplo foi extraído |
| `tg:sourceCharOffset` | Declaração | Deslocamento de caractere dentro do fragmento onde o texto de origem começa |
| `tg:sourceCharLength` | Declaração | Comprimento do texto de origem em caracteres |
#### Inicialização do Vocabulário (Por Coleção)
O grafo de conhecimento é neutro em relação à ontologia e é inicializado como vazio. Ao gravar dados de proveniência PROV-O em uma coleção pela primeira vez, o vocabulário deve ser inicializado com rótulos RDF para todas as classes e predicados. Isso garante a exibição legível por humanos em consultas e na interface do usuário.
**Classes 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" .
```
<<<<<<< HEAD
**Predicados TrustGraph:**
=======
**Predicados do TrustGraph:**
>>>>>>> 82edf2d (New md files from RunPod)
```
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
**Observação sobre a implementação:** Este processo de inicialização do vocabulário deve ser idempotente - seguro para executar várias vezes sem criar duplicatas. Pode ser acionado no processamento do primeiro documento em uma coleção, ou como uma etapa separada de inicialização da coleção.
=======
**Observação sobre a implementação:** Este vocabulário de inicialização deve ser idempotente - seguro para executar várias vezes sem criar duplicatas. Pode ser acionado no processamento do primeiro documento em uma coleção, ou como uma etapa separada de inicialização da coleção.
>>>>>>> 82edf2d (New md files from RunPod)
#### Proveniência de Sub-Fragmentos (Alvo)
Para uma rastreabilidade mais detalhada, seria valioso registrar exatamente onde, dentro de um fragmento, uma tripla foi extraída. Isso permite:
Destacar o texto de origem exato na interface do usuário
<<<<<<< HEAD
Verificar a precisão da extração em relação à fonte
=======
Verificar a precisão da extração em relação à origem
>>>>>>> 82edf2d (New md files from RunPod)
Depurar a qualidade da extração no nível da frase
**Exemplo com rastreamento de posição:**
```
# 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 .
```
**Exemplo com intervalo 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" .
```
**Considerações de implementação:**
A extração baseada em LLM pode não fornecer naturalmente as posições dos caracteres.
Poderia solicitar ao LLM que retornasse a frase/frase de origem junto com as triplas extraídas.
<<<<<<< HEAD
Alternativamente, pós-processe para fazer uma correspondência aproximada das entidades extraídas com o texto de origem.
=======
Alternativamente, pós-processar para fazer uma correspondência aproximada das entidades extraídas com o texto de origem.
>>>>>>> 82edf2d (New md files from RunPod)
Compromisso entre a complexidade da extração e a granularidade da procedência.
Pode ser mais fácil de alcançar com métodos de extração estruturados do que com a extração de LLM de formato livre.
Isso está marcado como uma meta a longo prazo - a procedência no nível do bloco deve ser implementada primeiro, com o rastreamento de subblocos como um aprimoramento futuro, se viável.
### Modelo de Armazenamento Duplo
O grafo de procedência é construído progressivamente à medida que os documentos fluem pelo pipeline:
| Armazenamento | O que é armazenado | Propósito |
|-------|---------------|---------|
<<<<<<< HEAD
| Bibliotecário | Conteúdo do documento + links pai-filho | Recuperação de conteúdo, exclusão em cascata |
| Grafo de Conhecimento | Arestas pai-filho + metadados | Consultas de procedência, atribuição de fatos |
Ambos os armazenamentos mantêm a mesma estrutura de grafo. O bibliotecário armazena o conteúdo; o grafo armazena os relacionamentos e permite consultas de travessia.
### Princípios de Design Chave
1. **ID do documento como a unidade de fluxo** - Os processadores passam IDs, não o conteúdo. O conteúdo é buscado do bibliotecário quando necessário.
=======
| Librarian | Conteúdo do documento + links pai-filho | Recuperação de conteúdo, exclusão em cascata |
| Knowledge Graph | Arestas pai-filho + metadados | Consultas de procedência, atribuição de fatos |
Ambos os armazenamentos mantêm a mesma estrutura de grafo. O librarian armazena o conteúdo; o grafo armazena os relacionamentos e permite consultas de travessia.
### Princípios de Design Chave
1. **ID do documento como a unidade de fluxo** - Os processadores passam IDs, não o conteúdo. O conteúdo é buscado do librarian quando necessário.
>>>>>>> 82edf2d (New md files from RunPod)
2. **Emitir uma vez na origem** - Os metadados são gravados no grafo uma vez quando o processamento começa, e não repetidos downstream.
3. **Padrão de processador consistente** - Cada processador segue o mesmo padrão de receber/buscar/produzir/salvar/emitir/transmitir.
4. **Construção progressiva do grafo** - Cada processador adiciona seu nível ao grafo. A cadeia completa de procedência é construída incrementalmente.
<<<<<<< HEAD
5. **Otimização pós-fragmentação** - Após a fragmentação, as mensagens carregam tanto o ID quanto o conteúdo. Os fragmentos são pequenos (2-4 KB), portanto, incluir o conteúdo evita viagens de ida e volta desnecessárias ao bibliotecário, preservando a procedência por meio do ID.
## Tarefas de Implementação
### Alterações no Bibliotecário
=======
5. **Otimização pós-fragmentação** - Após a fragmentação, as mensagens carregam tanto o ID quanto o conteúdo. Os fragmentos são pequenos (2-4 KB), portanto, incluir o conteúdo evita viagens de ida e volta desnecessárias ao librarian, preservando a procedência por meio do ID.
## Tarefas de Implementação
### Alterações no Librarian
>>>>>>> 82edf2d (New md files from RunPod)
#### Estado Atual
Inicia o processamento do documento enviando o ID do documento para o primeiro processador.
<<<<<<< HEAD
Não possui conexão com o armazenamento de triplas - os metadados são agrupados com as saídas de extração.
=======
Não possui conexão com o triple store - os metadados são agrupados com as saídas de extração.
>>>>>>> 82edf2d (New md files from RunPod)
`add-child-document` cria links pai-filho de um nível.
`list-children` retorna apenas os filhos imediatos.
#### Alterações Necessárias
<<<<<<< HEAD
**1. Nova interface: Conexão com o armazenamento de triplas**
O bibliotecário precisa emitir as bordas de metadados do documento diretamente para o grafo de conhecimento ao iniciar o processamento.
Adicione um cliente/publicador de armazenamento de triplas ao serviço do bibliotecário.
Na inicialização do processamento: emita os metadados do documento raiz como bordas do grafo (uma vez).
**2. Vocabulário de tipo de documento**
Padronize os valores de `document_type` para documentos filhos:
=======
**1. Nova interface: Conexão com o triple store**
O librarian precisa emitir as bordas de metadados do documento diretamente para o knowledge graph ao iniciar o processamento.
Adicionar cliente/publicador do triple store ao serviço do librarian.
Na inicialização do processamento: emitir os metadados do documento raiz como bordas do grafo (uma vez).
**2. Vocabulário de tipo de documento**
Padronizar os valores de `document_type` para documentos filhos:
>>>>>>> 82edf2d (New md files from RunPod)
`source` - documento original carregado.
`page` - página extraída da fonte (PDF, etc.).
`chunk` - fragmento de texto derivado da página ou da fonte.
#### Resumo das Alterações na Interface
| Interface | Alteração |
|-----------|--------|
<<<<<<< HEAD
| Armazenamento de triplas | Nova conexão de saída - emita bordas de metadados do documento |
| Início do processamento | Emita metadados para o grafo antes de encaminhar o ID do documento |
=======
| Triple store | Nova conexão de saída - emitir bordas de metadados do documento |
| Início do processamento | Emitir metadados para o grafo antes de encaminhar o ID do documento |
>>>>>>> 82edf2d (New md files from RunPod)
### Alterações no Extrator de PDF
#### Estado Atual
Recebe o conteúdo do documento (ou transmite documentos grandes).
Extrai texto das páginas PDF.
Transmite o conteúdo da página para o fragmentador.
<<<<<<< HEAD
Não interage com o bibliotecário ou o armazenamento de triplas.
#### Alterações Necessárias
**1. Nova interface: Cliente do bibliotecário**
O extrator de PDF precisa salvar cada página como um documento filho no bibliotecário.
Adicione um cliente do bibliotecário ao serviço do extrator de PDF.
Para cada página: chame `add-child-document` com pai = ID do documento raiz.
**2. Nova interface: Conexão com o armazenamento de triplas**
O extrator de PDF precisa emitir bordas pai-filho para o grafo de conhecimento.
Adicione um cliente/publicador de armazenamento de triplas.
Para cada página: emita uma borda que vincule o documento da página ao documento pai.
=======
Não interage com o librarian ou o triple store.
#### Alterações Necessárias
**1. Nova interface: Cliente do librarian**
O extrator de PDF precisa salvar cada página como um documento filho no librarian.
Adicionar cliente do librarian ao serviço do extrator de PDF.
Para cada página: chamar `add-child-document` com pai = ID do documento raiz.
**2. Nova interface: Conexão com o triple store**
O extrator de PDF precisa emitir bordas pai-filho para o knowledge graph.
Adicionar cliente/publicador do triple store.
Para cada página: emitir uma borda que vincule o documento da página ao documento pai.
>>>>>>> 82edf2d (New md files from RunPod)
**3. Alterar o formato de saída**
Em vez de encaminhar o conteúdo da página diretamente, encaminhe o ID do documento da página.
O Chunker buscará o conteúdo do "librarian" usando o ID.
#### Resumo das Alterações na Interface
| Interface | Mudança |
|-----------|--------|
| Librarian | Nova saída - salvar documentos filhos |
| Triple store | Nova saída - emitir arestas pai-filho |
| Mensagem de saída | Mudança de conteúdo para ID do documento |
### Mudanças no Chunker
#### Estado Atual
Recebe conteúdo da página/texto
Divide em partes (chunks)
Encaminha o conteúdo da parte para processadores subsequentes
Sem interação com o "librarian" ou o "triple store"
#### Mudanças Necessárias
**1. Alterar o tratamento da entrada**
Receber o ID do documento em vez do conteúdo, buscar do "librarian".
Adicionar cliente do "librarian" ao serviço do Chunker
Buscar o conteúdo da página usando o ID do documento
**2. Nova interface: Cliente do "Librarian" (escrita)**
Salvar cada parte como um documento filho no "librarian".
Para cada parte: chamar `add-child-document` com parent = ID do documento da página
**3. Nova interface: Conexão com o "Triple store"**
Emitir arestas pai-filho para o grafo de conhecimento.
Adicionar cliente/publicador do "triple store"
Para cada parte: emitir aresta ligando o documento da parte ao documento da página
**4. Alterar o formato de saída**
Encaminhar tanto o ID do documento da parte quanto o conteúdo da parte (otimização pós-chunker).
Os processadores subsequentes recebem o ID para rastreabilidade + o conteúdo para trabalhar
#### Resumo das Alterações na Interface
| Interface | Mudança |
|-----------|--------|
| Mensagem de entrada | Mudança de conteúdo para ID do documento |
| Librarian | Nova saída (leitura + escrita) - buscar conteúdo, salvar documentos filhos |
| Triple store | Nova saída - emitir arestas pai-filho |
| Mensagem de saída | Mudança de conteúdo-apenas para ID + conteúdo |
### Mudanças no Extrator de Conhecimento
#### Estado Atual
Recebe conteúdo da parte
Extrai triplas e embeddings
Emite para o "triple store" e o "embedding store"
A relação `subjectOf` aponta para o documento de nível superior (não para a parte)
#### Mudanças Necessárias
**1. Alterar o tratamento da entrada**
Receber o ID do documento da parte junto com o conteúdo.
Usar o ID da parte para rastreabilidade (o conteúdo já está incluído por otimização)
**2. Atualizar a rastreabilidade das triplas**
Ligar as triplas extraídas à parte (não ao documento de nível superior).
Usar a reificação para criar uma aresta apontando para a aresta
Relação `subjectOf`: tripla → ID do documento da parte
Primeiro uso do suporte de reificação existente
**3. Atualizar a rastreabilidade dos embeddings**
Ligar os IDs das entidades de embedding à parte.
Emitir aresta: ID da entidade de embedding → ID do documento da parte
#### Resumo das Alterações na Interface
| Interface | Mudança |
|-----------|--------|
| Mensagem de entrada | Esperar ID da parte + conteúdo (não apenas conteúdo) |
| Triple store | Usar reificação para rastreabilidade de tripla → parte |
| Rastreabilidade de embedding | Ligar ID da entidade → ID da parte |
## Referências
Rastreabilidade em tempo de consulta: `docs/tech-specs/query-time-provenance.md`
Padrão PROV-O para modelagem de rastreabilidade
Metadados de origem existentes no grafo de conhecimento (precisa de auditoria)

View file

@ -0,0 +1,317 @@
---
layout: default
title: "Especificação da Definição do Modelo de Fluxo"
parent: "Portuguese (Beta)"
---
# Especificação da Definição do Modelo de Fluxo
> **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.
## Visão Geral
Um modelo de fluxo define um modelo de padrão de fluxo de dados completo no sistema TrustGraph. Quando instanciado, ele cria uma rede interconectada de processadores que lidam com a ingestão, o processamento, o armazenamento e a consulta de dados como um sistema unificado.
## Estrutura
Uma definição de modelo de fluxo consiste em cinco seções principais:
### 1. Seção de Classe
Define processadores de serviço compartilhados que são instanciados uma vez por modelo de fluxo. Esses processadores lidam com solicitações de todas as instâncias de fluxo desta classe.
```json
"class": {
"service-name:{class}": {
"request": "queue-pattern:{class}",
"response": "queue-pattern:{class}",
"settings": {
"setting-name": "fixed-value",
"parameterized-setting": "{parameter-name}"
}
}
}
```
**Características:**
Compartilhadas entre todas as instâncias de fluxo da mesma classe.
<<<<<<< HEAD
Normalmente, serviços caros ou sem estado (LLMs, modelos de embedding).
=======
Normalmente, serviços com alto custo ou sem estado (LLMs, modelos de embedding).
>>>>>>> 82edf2d (New md files from RunPod)
Use a variável de modelo `{class}` para o nome da fila.
As configurações podem ser valores fixos ou parametrizadas com a sintaxe `{parameter-name}`.
Exemplos: `embeddings:{class}`, `text-completion:{class}`, `graph-rag:{class}`.
### 2. Seção de Fluxo
Define processadores específicos do fluxo que são instanciados para cada instância de fluxo individual. Cada fluxo recebe seu próprio conjunto isolado desses processadores.
```json
"flow": {
"processor-name:{id}": {
"input": "queue-pattern:{id}",
"output": "queue-pattern:{id}",
"settings": {
"setting-name": "fixed-value",
"parameterized-setting": "{parameter-name}"
}
}
}
```
**Características:**
Instância única por fluxo
Gerenciar dados e estado específicos do fluxo
<<<<<<< HEAD
Usar variável de modelo `{id}` para nomeação de filas
As configurações podem ser valores fixos ou parametrizados com a sintaxe `{parameter-name}`
Exemplos: `chunker:{id}`, `pdf-decoder:{id}`, `kg-extract-relationships:{id}`
### 3. Seção de Interfaces
Define os pontos de entrada e os contratos de interação para o fluxo. Estes formam a superfície da API para sistemas externos e comunicação entre componentes internos.
=======
Usar variável de modelo `{id}` para nomeação de fila
As configurações podem ser valores fixos ou parametrizadas com a sintaxe `{parameter-name}`
Exemplos: `chunker:{id}`, `pdf-decoder:{id}`, `kg-extract-relationships:{id}`
### 3. Seção de Interfaces
Define os pontos de entrada e os contratos de interação para o fluxo. Estes formam a superfície da API para sistemas externos e comunicação de componentes internos.
>>>>>>> 82edf2d (New md files from RunPod)
As interfaces podem assumir duas formas:
**Padrão Fire-and-Forget** (uma única fila):
```json
"interfaces": {
"document-load": "persistent://tg/flow/document-load:{id}",
"triples-store": "persistent://tg/flow/triples-store:{id}"
}
```
**Padrão de Requisição/Resposta** (objeto com campos de requisição/resposta):
```json
"interfaces": {
"embeddings": {
"request": "non-persistent://tg/request/embeddings:{class}",
"response": "non-persistent://tg/response/embeddings:{class}"
}
}
```
**Tipos de Interfaces:**
**Pontos de Entrada**: Onde sistemas externos injetam dados (`document-load`, `agent`)
**Interfaces de Serviço**: Padrões de solicitação/resposta para serviços (`embeddings`, `text-completion`)
**Interfaces de Dados**: Pontos de conexão de fluxo de dados do tipo "enviar e esquecer" (`triples-store`, `entity-contexts-load`)
### 4. Seção de Parâmetros
Mapeia nomes de parâmetros específicos do fluxo para definições de parâmetros armazenadas centralmente:
```json
"parameters": {
"model": "llm-model",
"temp": "temperature",
"chunk": "chunk-size"
}
```
**Características:**
As chaves são nomes de parâmetros usados nas configurações do processador (por exemplo, `{model}`)
Os valores referenciam as definições de parâmetros armazenadas em schema/config
Permite a reutilização de definições de parâmetros comuns em diferentes fluxos
Reduz a duplicação de esquemas de parâmetros
### 5. Metadados
Informações adicionais sobre o blueprint do fluxo:
```json
"description": "Human-readable description",
"tags": ["capability-1", "capability-2"]
```
## Variáveis de Modelo
### Variáveis do Sistema
#### {id}
<<<<<<< HEAD
Substituído pelo identificador de instância de fluxo único.
=======
Substituído pelo identificador único da instância do fluxo.
>>>>>>> 82edf2d (New md files from RunPod)
Cria recursos isolados para cada fluxo.
Exemplo: `flow-123`, `customer-A-flow`
#### {class}
Substituído pelo nome do blueprint do fluxo.
Cria recursos compartilhados entre fluxos da mesma classe.
Exemplo: `standard-rag`, `enterprise-rag`
### Variáveis de Parâmetro
#### {parameter-name}
Parâmetros personalizados definidos no momento da execução do fluxo.
Os nomes dos parâmetros correspondem às chaves na seção `parameters` do fluxo.
Usados em configurações do processador para personalizar o comportamento.
Exemplos: `{model}`, `{temp}`, `{chunk}`
Substituído pelos valores fornecidos ao iniciar o fluxo.
Validado em relação às definições de parâmetros armazenadas centralmente.
## Configurações do Processador
As configurações fornecem valores de configuração aos processadores no momento da instanciação. Elas podem ser:
### Configurações Fixas
Valores diretos que não mudam:
```json
"settings": {
"model": "gemma3:12b",
"temperature": 0.7,
"max_retries": 3
}
```
### Configurações Parametrizadas
Valores que utilizam parâmetros fornecidos no lançamento do fluxo:
```json
"settings": {
"model": "{model}",
"temperature": "{temp}",
"endpoint": "https://{region}.api.example.com"
}
```
Os nomes dos parâmetros nas configurações correspondem às chaves na seção `parameters` do fluxo.
### Exemplos de Configurações
**Processador LLM com 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 com Configurações Fixas e 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"
}
}
```
## Padrões de Filas (Pulsar)
Os modelos de fluxo utilizam o Apache Pulsar para mensagens. Os nomes das filas seguem o formato do Pulsar:
```
<persistence>://<tenant>/<namespace>/<topic>
```
### Componentes:
**persistência**: `persistent` ou `non-persistent` (modo de persistência do Pulsar)
<<<<<<< HEAD
**inquilino**: `tg` para definições de blueprint de fluxo fornecidas pelo TrustGraph
=======
**inquilino**: `tg` para definições de blueprint de fluxo fornecidas pela TrustGraph
>>>>>>> 82edf2d (New md files from RunPod)
**namespace**: Indica o padrão de mensagens
`flow`: Serviços de envio e esquecimento
`request`: Parte de solicitação de serviços de solicitação/resposta
`response`: Parte de resposta de serviços de solicitação/resposta
**tópico**: O nome específico da fila/tópico com variáveis de modelo
### Filas Persistentes
Padrão: `persistent://tg/flow/<topic>:{id}`
Usado para serviços de envio e esquecimento e fluxo de dados durável
Os dados persistem no armazenamento do Pulsar durante as reinicializações
Exemplo: `persistent://tg/flow/chunk-load:{id}`
### Filas Não Persistentes
Padrão: `non-persistent://tg/request/<topic>:{class}` ou `non-persistent://tg/response/<topic>:{class}`
Usado para padrões de mensagens de solicitação/resposta
Efêmeras, não persistidas em disco pelo Pulsar
Menor latência, adequado para comunicação no estilo RPC
Exemplo: `non-persistent://tg/request/embeddings:{class}`
## Arquitetura do Fluxo de Dados
O blueprint do fluxo cria um fluxo de dados unificado onde:
<<<<<<< HEAD
1. **Pipeline de Processamento de Documentos**: Fluxo da ingestão ao processamento e armazenamento
2. **Serviços de Consulta**: Processadores integrados que consultam os mesmos armazenamentos de dados e serviços
3. **Serviços Compartilhados**: Processadores centralizados que todos os fluxos podem utilizar
4. **Escritores de Armazenamento**: Persistem os dados processados em armazenamentos apropriados
=======
1. **Pipeline de Processamento de Documentos**: Fluxo da ingestão ao armazenamento, passando pela transformação
2. **Serviços de Consulta**: Processadores integrados que consultam os mesmos armazenamentos de dados e serviços
3. **Serviços Compartilhados**: Processadores centralizados que todos os fluxos podem utilizar
4. **Escritores de Armazenamento**: Persistem os dados processados nos armazenamentos apropriados
>>>>>>> 82edf2d (New md files from RunPod)
Todos os processadores (tanto `{id}` quanto `{class}`) trabalham juntos como um grafo de fluxo de dados coeso, e não como sistemas separados.
## Exemplo de Instanciação de Fluxo
Dado:
ID da Instância do Fluxo: `customer-A-flow`
Blueprint do Fluxo: `standard-rag`
Mapeamentos de parâmetros do fluxo:
`"model": "llm-model"`
`"temp": "temperature"`
`"chunk": "chunk-size"`
Parâmetros fornecidos pelo usuário:
`model`: `gpt-4`
`temp`: `0.5`
`chunk`: `512`
Expansões de modelo:
`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"`
Isso cria:
Pipeline de processamento de documentos isolado para `customer-A-flow`
Serviço de incorporação compartilhado para todos os fluxos `standard-rag`
<<<<<<< HEAD
Fluxo de dados completo da ingestão de documentos à consulta
=======
Fluxo de dados completo da ingestão do documento à consulta
>>>>>>> 82edf2d (New md files from RunPod)
Processadores configurados com os valores de parâmetro fornecidos
## Benefícios
1. **Eficiência de Recursos**: Serviços caros são compartilhados entre os fluxos
2. **Isolamento de Fluxo**: Cada fluxo tem seu próprio pipeline de processamento de dados
3. **Escalabilidade**: É possível instanciar vários fluxos do mesmo modelo
4. **Modularidade**: Separação clara entre componentes compartilhados e específicos do fluxo
5. **Arquitetura Unificada**: Consulta e processamento fazem parte do mesmo fluxo de dados

View file

@ -0,0 +1,596 @@
---
layout: default
title: "Especificação Técnica de Parâmetros Configuráveis para Flow Blueprint"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Parâmetros Configuráveis 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.
## Visão Geral
Esta especificação descreve a implementação de parâmetros configuráveis para flow blueprints no TrustGraph. Os parâmetros permitem que os usuários personalizem os parâmetros do processador no momento da execução do fluxo, fornecendo valores que substituem os espaços reservados de parâmetros na definição do flow blueprint.
Os parâmetros funcionam por meio da substituição de variáveis de modelo nos parâmetros do processador, de forma semelhante a como as variáveis `{id}` e `{class}` funcionam, mas com valores fornecidos pelo usuário.
A integração suporta quatro casos de uso principais:
1. **Seleção de Modelo**: Permitindo que os usuários escolham diferentes modelos LLM (por exemplo, `gemma3:8b`, `gpt-4`, `claude-3`) para processadores.
2. **Configuração de Recursos**: Ajustando parâmetros do processador, como tamanhos de lote, tamanhos de lote e limites de concorrência.
3. **Ajuste de Comportamento**: Modificando o comportamento do processador por meio de parâmetros como temperatura, max-tokens ou limites de recuperação.
4. **Parâmetros Específicos do Ambiente**: Configurando endpoints, chaves de API ou URLs específicas da região para cada implantação.
## Objetivos
**Configuração Dinâmica do Processador**: Permitir a configuração em tempo de execução dos parâmetros do processador por meio da substituição de parâmetros.
**Validação de Parâmetros**: Fornecer verificação de tipo e validação para parâmetros no momento da execução do fluxo.
**Valores Padrão**: Suportar valores padrão sensatos, permitindo a substituição para usuários avançados.
**Substituição de Modelo**: Substituir perfeitamente os espaços reservados de parâmetros nos parâmetros do processador.
**Integração com a Interface do Usuário**: Permitir a entrada de parâmetros por meio de interfaces de API e de interface do usuário.
**Segurança de Tipo**: Garantir que os tipos de parâmetros correspondam aos tipos de parâmetros do processador esperados.
**Documentação**: Esquemas de parâmetros autoexplicativos dentro das definições do flow blueprint.
**Compatibilidade com Versões Anteriores**: Manter a compatibilidade com flow blueprints existentes que não usam parâmetros.
## Contexto
Flow blueprints no TrustGraph agora suportam parâmetros de processador que podem conter valores fixos ou espaços reservados de parâmetros. Isso cria uma oportunidade para personalização em tempo de execução.
Os parâmetros de processador atuais suportam:
Valores fixos: `"model": "gemma3:12b"`
Espaços reservados de parâmetros: `"model": "gemma3:{model-size}"`
Esta especificação define como os parâmetros são:
Declarados em definições de flow blueprint.
Validados quando os fluxos são iniciados.
Substituídos em parâmetros do processador.
Expostos por meio de APIs e da interface do usuário.
Ao aproveitar os parâmetros de processador parametrizados, o TrustGraph pode:
Reduzir a duplicação de flow blueprints, usando parâmetros para variações.
Permitir que os usuários ajustem o comportamento do processador sem modificar as definições.
Suportar configurações específicas do ambiente por meio de valores de parâmetro.
Manter a segurança de tipo por meio da validação do esquema de parâmetro.
## Design Técnico
### Arquitetura
O sistema de parâmetros configuráveis requer os seguintes componentes técnicos:
1. **Definição de Esquema de Parâmetro**
Definições de parâmetros baseadas em JSON Schema dentro dos metadados do flow blueprint.
Definições de tipo, incluindo string, número, booleano, enum e tipos de objeto.
Regras de validação, incluindo valores mínimo/máximo, padrões e campos obrigatórios.
Módulo: trustgraph-flow/trustgraph/flow/definition.py
2. **Motor de Resolução de Parâmetros**
Validação de parâmetros em tempo de execução contra o esquema.
Aplicação de valores padrão para parâmetros não especificados.
Injeção de parâmetros no contexto de execução do fluxo.
Coerção e conversão de tipo, conforme necessário.
Módulo: trustgraph-flow/trustgraph/flow/parameter_resolver.py
3. **Integração com o Armazenamento de Parâmetros**
Recuperação de definições de parâmetros do armazenamento de esquema/configuração.
Cache de definições de parâmetros frequentemente usadas.
Validação contra esquemas armazenados centralmente.
Módulo: trustgraph-flow/trustgraph/flow/parameter_store.py
4. **Extensões do Lançador de Fluxo**
Extensões de API para aceitar valores de parâmetro durante o lançamento do fluxo.
Resolução de mapeamento de parâmetros (nomes de fluxo para nomes de definição).
Tratamento de erros para combinações de parâmetros inválidas.
Módulo: trustgraph-flow/trustgraph/flow/launcher.py
<<<<<<< HEAD
5. **Formulários de Parâmetro da Interface do Usuário**
Geração dinâmica de formulários a partir de metadados de parâmetros do fluxo.
Exibição ordenada de parâmetros usando o campo `order`.
Rótulos de parâmetros descritivos usando o campo `description`.
=======
5. **Formulários de Parâmetros da Interface do Usuário**
Geração dinâmica de formulários a partir de metadados de parâmetros do fluxo.
Exibição ordenada de parâmetros usando o campo `order`.
Rótulos descritivos de parâmetros usando o campo `description`.
>>>>>>> 82edf2d (New md files from RunPod)
Validação de entrada contra as definições de tipo de parâmetro.
Predefinições e modelos de parâmetros.
Módulo: trustgraph-ui/components/flow-parameters/
### Modelos de Dados
#### Definições de Parâmetro (Armazenadas no Esquema/Configuração)
As definições de parâmetros são armazenadas centralmente no sistema de esquema e configuração com o 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 fluxo com referências de parâmetros
Os diagramas de fluxo definem metadados de parâmetros com referências de tipo, descrições e ordem:
```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}"
}
}
}
}
```
A seção `parameters` mapeia nomes de parâmetros específicos do fluxo (chaves) para objetos de metadados de parâmetros que contêm:
`type`: Referência à definição de parâmetro definida centralmente (por exemplo, "llm-model")
`description`: Descrição legível por humanos para exibição na interface do usuário
`order`: Ordem de exibição para formulários de parâmetros (números menores aparecem primeiro)
`advanced` (opcional): Sinalizador booleano que indica se este é um parâmetro avançado (padrão: falso). Quando definido como verdadeiro, a interface do usuário pode ocultar este parâmetro por padrão ou colocá-lo em uma seção "Avançado"
`controlled-by` (opcional): Nome de outro parâmetro que controla o valor deste parâmetro quando no modo simples. Quando especificado, este parâmetro herda seu valor do parâmetro de controle, a menos que seja explicitamente substituído
Esta abordagem permite:
Definições de tipos de parâmetros reutilizáveis em vários modelos de fluxo
Gerenciamento e validação centralizados de tipos de parâmetros
Descrições e ordenação de parâmetros específicos do fluxo
Experiência de interface do usuário aprimorada com formulários de parâmetros descritivos
Validação consistente de parâmetros em todos os fluxos
Adição fácil de novos tipos de parâmetros padrão
Interface do usuário simplificada com separação de modo básico/avançado
Herança de valores de parâmetros para configurações relacionadas
#### Solicitação de Inicialização do Fluxo
A API de inicialização do fluxo aceita parâmetros usando os nomes de parâmetros do fluxo:
```json
{
"flow_class": "document-analysis",
"flow_id": "customer-A-flow",
"parameters": {
"llm-model": "claude-3",
"llm-temperature": 0.5,
"chunk-size": 1024
}
}
```
Nota: Neste exemplo, `llm-rag-model` não é fornecido explicitamente, mas herdará o valor "claude-3" de `llm-model` devido à sua relação `controlled-by`. Da mesma forma, `chunk-overlap` pode herdar um valor calculado com base em `chunk-size`.
O sistema irá:
<<<<<<< HEAD
1. Extrair metadados de parâmetros da definição do blueprint do fluxo
2. Mapear os nomes dos parâmetros do fluxo para suas definições de tipo (por exemplo, `llm-model``llm-model` tipo)
3. Resolver as relações "controlado por" (por exemplo, `llm-rag-model` herda de `llm-model`)
4. Validar os valores fornecidos pelo usuário e os valores herdados em relação às definições de tipo dos parâmetros
5. Substituir os valores resolvidos nos parâmetros do processador durante a instanciação do fluxo
=======
1. Extrair metadados de parâmetros da definição do fluxo.
2. Mapear os nomes dos parâmetros do fluxo para suas definições de tipo (por exemplo, `llm-model``llm-model` tipo).
3. Resolver relações de dependência (por exemplo, `llm-rag-model` herda de `llm-model`).
4. Validar os valores fornecidos pelo usuário e os valores herdados em relação às definições de tipo dos parâmetros.
5. Substituir os valores resolvidos nos parâmetros do processador durante a instanciação do fluxo.
>>>>>>> 82edf2d (New md files from RunPod)
### Detalhes da Implementação
#### Processo de Resolução de Parâmetros
Quando um fluxo é iniciado, o sistema executa as seguintes etapas de resolução de parâmetros:
<<<<<<< HEAD
1. **Carregamento do Blueprint do Fluxo**: Carregar a definição do blueprint do fluxo e extrair os metadados dos parâmetros
2. **Extração de Metadados**: Extrair `type`, `description`, `order`, `advanced` e `controlled-by` para cada parâmetro definido na seção `parameters` do blueprint do fluxo
3. **Consulta da Definição de Tipo**: Para cada parâmetro no blueprint do fluxo:
Recuperar a definição de tipo do parâmetro do armazenamento de esquema/configuração usando o campo `type`
As definições de tipo são armazenadas com o tipo "parameter-type" no sistema de configuração
Cada definição de tipo contém o esquema do parâmetro, o valor padrão e as regras de validação
4. **Resolução do Valor Padrão**:
Para cada parâmetro definido no blueprint do fluxo:
Verificar se o usuário forneceu um valor para este parâmetro
Se nenhum valor do usuário for fornecido, usar o valor `default` da definição de tipo do parâmetro
Criar um mapa de parâmetros completo contendo tanto os valores fornecidos pelo usuário quanto os valores padrão
5. **Resolução de Herança de Parâmetros** (relações "controlado por"):
Para parâmetros com o campo `controlled-by`, verificar se um valor foi fornecido explicitamente
Se nenhum valor explícito for fornecido, herdar o valor do parâmetro de controle
Se o parâmetro de controle também não tiver valor, usar o padrão da definição de tipo
Validar que não existam dependências circulares nas relações `controlled-by`
6. **Validação**: Validar o conjunto completo de parâmetros (fornecidos pelo usuário, padrões e herdados) em relação às definições de tipo
7. **Armazenamento**: Armazenar o conjunto completo de parâmetros resolvidos com a instância do fluxo para auditoria
8. **Substituição de Modelos**: Substituir os espaços reservados de parâmetros nos parâmetros do processador com os valores resolvidos
9. **Instanciação do Processador**: Criar processadores com os parâmetros substituídos
**Notas Importantes de Implementação:**
O serviço de fluxo DEVE mesclar os parâmetros fornecidos pelo usuário com os padrões das definições de tipo de parâmetro
O conjunto completo de parâmetros (incluindo os padrões aplicados) DEVE ser armazenado com o fluxo para rastreabilidade
A resolução de parâmetros ocorre no início do fluxo, não no momento da instanciação do processador
Parâmetros obrigatórios sem padrões DEVE causar a falha no início do fluxo com uma mensagem de erro clara
#### Herança de Parâmetros com "controlado-por"
O campo `controlled-by` permite a herança de valores de parâmetros, o que é particularmente útil para simplificar as interfaces do usuário, mantendo a flexibilidade:
**Cenário de Exemplo**:
O parâmetro `llm-model` controla o modelo LLM primário
O parâmetro `llm-rag-model` tem `"controlled-by": "llm-model"`
No modo simples, definir `llm-model` para "gpt-4" define automaticamente `llm-rag-model` para "gpt-4" também
No modo avançado, os usuários podem substituir `llm-rag-model` com um valor diferente
**Regras de Resolução**:
1. Se um parâmetro tiver um valor fornecido explicitamente, use esse valor
2. Se não houver valor explícito e `controlled-by` estiver definido, use o valor do parâmetro de controle
3. Se o parâmetro de controle não tiver valor, use o padrão da definição de tipo
4. Dependências circulares nas relações `controlled-by` resultam em um erro de validação
**Comportamento da IU**:
No modo básico/simples: Parâmetros com `controlled-by` podem ser ocultos ou exibidos como somente leitura com valor herdado
No modo avançado: Todos os parâmetros são exibidos e podem ser configurados individualmente
Quando um parâmetro de controle é alterado, os parâmetros dependentes são atualizados automaticamente, a menos que sejam explicitamente substituídos
=======
1. **Carregamento da Definição do Fluxo**: Carregar a definição do fluxo e extrair os metadados dos parâmetros.
2. **Extração de Metadados**: Extrair `type`, `description`, `order`, `advanced` e `controlled-by` para cada parâmetro definido na seção `parameters` da definição do fluxo.
3. **Consulta da Definição de Tipo**: Para cada parâmetro na definição do fluxo:
Recuperar a definição de tipo do parâmetro do armazenamento de esquema/configuração usando o campo `type`.
As definições de tipo são armazenadas com o tipo "parameter-type" no sistema de configuração.
Cada definição de tipo contém o esquema do parâmetro, o valor padrão e as regras de validação.
4. **Resolução do Valor Padrão**:
Para cada parâmetro definido na definição do fluxo:
Verificar se o usuário forneceu um valor para este parâmetro.
Se nenhum valor do usuário for fornecido, usar o valor `default` da definição de tipo do parâmetro.
Criar um mapa de parâmetros completo contendo tanto os valores fornecidos pelo usuário quanto os valores padrão.
5. **Resolução de Herança de Parâmetros** (relações de dependência):
Para parâmetros com o campo `controlled-by`, verificar se um valor foi fornecido explicitamente.
Se nenhum valor explícito for fornecido, herdar o valor do parâmetro de controle.
Se o parâmetro de controle também não tiver valor, usar o padrão da definição de tipo.
Validar que não existam dependências circulares nas relações `controlled-by`.
6. **Validação**: Validar o conjunto completo de parâmetros (fornecidos pelo usuário, padrões e herdados) em relação às definições de tipo.
7. **Armazenamento**: Armazenar o conjunto completo de parâmetros resolvidos com a instância do fluxo para auditoria.
8. **Substituição de Marcadores**: Substituir os marcadores de parâmetros nos parâmetros do processador pelos valores resolvidos.
9. **Instanciação do Processador**: Criar processadores com os parâmetros substituídos.
**Notas Importantes de Implementação:**
O serviço de fluxo DEVE mesclar os parâmetros fornecidos pelo usuário com os padrões das definições de tipo de parâmetro.
O conjunto completo de parâmetros (incluindo os padrões aplicados) DEVE ser armazenado com o fluxo para rastreabilidade.
A resolução de parâmetros ocorre no início do fluxo, não no momento da instanciação do processador.
Parâmetros obrigatórios sem padrões DEVE causar a falha no início do fluxo com uma mensagem de erro clara.
#### Herança de Parâmetros com dependência
O campo `controlled-by` permite a herança de valores de parâmetros, o que é particularmente útil para simplificar interfaces de usuário, mantendo a flexibilidade:
**Cenário de Exemplo**:
O parâmetro `llm-model` controla o modelo LLM primário.
O parâmetro `llm-rag-model` tem o valor `"controlled-by": "llm-model"`.
No modo simples, definir `llm-model` para "gpt-4" define automaticamente `llm-rag-model` para "gpt-4" também.
No modo avançado, os usuários podem substituir `llm-rag-model` com um valor diferente.
**Regras de Resolução**:
1. Se um parâmetro tiver um valor fornecido explicitamente, use esse valor.
2. Se não houver valor explícito e `controlled-by` estiver definido, use o valor do parâmetro de controle.
3. Se o parâmetro de controle não tiver valor, use o padrão da definição de tipo.
4. Dependências circulares nas relações `controlled-by` resultam em um erro de validação.
**Comportamento da Interface do Usuário**:
No modo básico/simples: Parâmetros com `controlled-by` podem ser ocultos ou exibidos como somente leitura com valor herdado.
No modo avançado: Todos os parâmetros são exibidos e podem ser configurados individualmente.
Quando um parâmetro de controle é alterado, os parâmetros dependentes são atualizados automaticamente, a menos que sejam explicitamente substituídos.
>>>>>>> 82edf2d (New md files from RunPod)
#### Integração com Pulsar
1. **Operação Start-Flow**
<<<<<<< HEAD
A operação start-flow do Pulsar precisa aceitar um campo `parameters` contendo um mapa de valores de parâmetros
O esquema do Pulsar para a solicitação start-flow deve ser atualizado para incluir o campo opcional `parameters`
=======
A operação start-flow do Pulsar precisa aceitar um campo `parameters` contendo um mapa de valores de parâmetros.
O esquema do Pulsar para a solicitação start-flow deve ser atualizado para incluir o campo opcional `parameters`.
>>>>>>> 82edf2d (New md files from RunPod)
Exemplo de solicitação:
```json
{
"flow_class": "document-analysis",
"flow_id": "customer-A-flow",
"parameters": {
"model": "claude-3",
"size": "12b",
"temp": 0.5,
"chunk": 1024
}
}
```
2. **Operação Get-Flow**
O esquema Pulsar para a resposta do get-flow deve ser atualizado para incluir o campo `parameters`
Isso permite que os clientes recuperem os valores dos parâmetros que foram usados quando o fluxo foi iniciado.
Exemplo de resposta:
```json
{
"flow_id": "customer-A-flow",
"flow_class": "document-analysis",
"status": "running",
"parameters": {
"model": "claude-3",
"size": "12b",
"temp": 0.5,
"chunk": 1024
}
}
```
#### Implementação do Serviço de Fluxo
O serviço de configuração de fluxo (`trustgraph-flow/trustgraph/config/service/flow.py`) requer as seguintes melhorias:
1. **Função de Resolução 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 função deve:
Extrair metadados de parâmetros da seção `parameters` do blueprint do fluxo
Para cada parâmetro, buscar a definição de tipo no armazenamento de configuração
Aplicar valores padrão para quaisquer parâmetros não fornecidos pelo usuário
Lidar com relacionamentos de herança `controlled-by`
Retornar o conjunto completo de parâmetros
2. **Método `handle_start_flow` Modificado**
Chamar `resolve_parameters` após carregar o blueprint do fluxo
<<<<<<< HEAD
Usar o conjunto completo de parâmetros resolvidos para substituição de modelo
=======
Usar o conjunto completo de parâmetros resolvidos para a substituição de modelo
>>>>>>> 82edf2d (New md files from RunPod)
Armazenar o conjunto completo de parâmetros (não apenas os fornecidos pelo usuário) com o fluxo
Validar que todos os parâmetros obrigatórios tenham valores
3. **Busca de Tipo de Parâmetro**
As definições de tipo de parâmetro são armazenadas na configuração com o tipo "parameter-type"
Cada definição de tipo contém esquema, valor padrão e regras de validação
Armazenar em cache os tipos de parâmetro frequentemente usados para reduzir as consultas à configuração
#### Integração com o Sistema de Configuração
3. **Armazenamento de Objetos de Fluxo**
Quando um fluxo é adicionado ao sistema de configuração pelo componente de fluxo no gerenciador de configuração, o objeto de fluxo deve incluir os valores de parâmetros resolvidos
O gerenciador de configuração precisa armazenar tanto os parâmetros originais fornecidos pelo usuário quanto os valores resolvidos (com os padrões aplicados)
Os objetos de fluxo no sistema de configuração devem incluir:
<<<<<<< HEAD
`parameters`: Os valores finais de parâmetros resolvidos usados para o fluxo
=======
`parameters`: Os valores de parâmetros resolvidos finais usados para o fluxo
>>>>>>> 82edf2d (New md files from RunPod)
#### Integração com a CLI
4. **Comandos da CLI da Biblioteca**
Os comandos da CLI que iniciam fluxos precisam de suporte a parâmetros:
Aceitar valores de parâmetros por meio de flags de linha de comando ou arquivos de configuração
<<<<<<< HEAD
Validar parâmetros em relação às definições do blueprint do fluxo antes da submissão
=======
Validar os parâmetros em relação às definições do blueprint do fluxo antes da submissão
>>>>>>> 82edf2d (New md files from RunPod)
Suportar a entrada de arquivos de parâmetros (JSON/YAML) para conjuntos de parâmetros complexos
Os comandos da CLI que mostram fluxos precisam exibir informações de parâmetros:
Mostrar os valores de parâmetros usados quando o fluxo foi iniciado
Exibir os parâmetros disponíveis para um blueprint de fluxo
Mostrar os esquemas e padrões de validação de parâmetros
#### Integração com a Classe Base do Processador
5. **Suporte a ParameterSpec**
<<<<<<< HEAD
As classes base do processador precisam suportar a substituição de parâmetros por meio do mecanismo existente ParametersSpec
=======
As classes base do processador precisam suportar a substituição de parâmetros por meio do mecanismo ParametersSpec existente
>>>>>>> 82edf2d (New md files from RunPod)
A classe ParametersSpec (localizada no mesmo módulo que ConsumerSpec e ProducerSpec) deve ser aprimorada, se necessário, para suportar a substituição de modelos de parâmetros
Os processadores devem ser capazes de invocar ParametersSpec para configurar seus parâmetros com valores de parâmetros resolvidos no momento da inicialização do fluxo
A implementação de ParametersSpec precisa:
Aceitar configurações de parâmetros que contenham espaços reservados de parâmetros (por exemplo, `{model}`, `{temperature}`)
Suportar a substituição de parâmetros em tempo de execução quando o processador é instanciado
Validar que os valores substituídos correspondam aos tipos e restrições esperados
Fornecer tratamento de erros para referências de parâmetros ausentes ou inválidos
#### Regras de Substituição
Os parâmetros usam o formato `{parameter-name}` nos parâmetros do processador
Os nomes dos parâmetros nos parâmetros correspondem às chaves na seção `parameters` do fluxo
A substituição ocorre juntamente com a substituição de `{id}` e `{class}`
Referências de parâmetros inválidas resultam em erros no momento da inicialização
A validação de tipo ocorre com base na definição de parâmetro armazenada centralmente
**IMPORTANTE**: Todos os valores de parâmetros são armazenados e transmitidos como strings
Os números são convertidos em strings (por exemplo, `0.7` se torna `"0.7"`)
Os booleanos são convertidos em strings em letras minúsculas (por exemplo, `true` se torna `"true"`)
Isso é necessário pelo esquema do Pulsar que define `parameters = Map(String())`
Exemplo de resolução:
```
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)
```
## Estratégia de Testes
<<<<<<< HEAD
Testes unitários para validação do esquema de parâmetros
Testes de integração para substituição de parâmetros nos parâmetros do processador
Testes de ponta a ponta para iniciar fluxos com diferentes valores de parâmetros
Testes de interface do usuário para geração e validação de formulários de parâmetros
Testes de desempenho para fluxos com muitos parâmetros
Casos extremos: parâmetros ausentes, tipos inválidos, referências de parâmetros indefinidos
=======
Testes unitários para validação do esquema de parâmetros.
Testes de integração para substituição de parâmetros nos parâmetros do processador.
Testes de ponta a ponta para iniciar fluxos com diferentes valores de parâmetros.
Testes de interface do usuário para geração e validação de formulários de parâmetros.
Testes de desempenho para fluxos com muitos parâmetros.
Casos de borda: parâmetros ausentes, tipos inválidos, referências de parâmetros indefinidos.
>>>>>>> 82edf2d (New md files from RunPod)
## Plano de Migração
1. O sistema deve continuar a suportar modelos de fluxo sem parâmetros
declarados.
2. O sistema deve continuar a suportar fluxos sem parâmetros especificados:
Isso funciona para fluxos sem parâmetros e para fluxos com parâmetros
(eles têm valores padrão).
## Perguntas Abertas
P: Os parâmetros devem suportar objetos aninhados complexos ou devem se limitar a tipos simples?
R: Os valores dos parâmetros serão codificados como strings, provavelmente queremos
restringir a strings.
P: Os espaços reservados de parâmetros devem ser permitidos em nomes de filas ou apenas em
parâmetros?
<<<<<<< HEAD
R: Apenas em parâmetros para evitar injeções estranhas e casos extremos.
=======
R: Apenas em parâmetros para remover injeções estranhas e casos de borda.
>>>>>>> 82edf2d (New md files from RunPod)
P: Como lidar com conflitos entre nomes de parâmetros e variáveis do sistema, como
`id` e `class`?
R: Não é válido especificar "id" e "class" ao iniciar um fluxo.
P: Devemos suportar parâmetros calculados (derivados de outros parâmetros)?
<<<<<<< HEAD
R: Apenas substituição de strings para evitar injeções estranhas e casos extremos.
=======
R: Apenas substituição de strings para remover injeções estranhas e casos de borda.
>>>>>>> 82edf2d (New md files from RunPod)
## Referências
Especificação do Esquema JSON: https://json-schema.org/
Especificação da Definição do Modelo de Fluxo: docs/tech-specs/flow-class-definition.md

View file

@ -0,0 +1,678 @@
---
layout: default
title: "Especificação Técnica dos Contextos de Grafos"
parent: "Portuguese (Beta)"
---
# Especificação Técnica dos 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.
## Visão Geral
Esta especificação descreve as alterações nos primitivos de grafo principais do TrustGraph para
estar em conformidade com o RDF 1.2 e suportar a semântica completa do Conjunto de Dados RDF. Esta é uma
alteração que causa incompatibilidade para a série de lançamento 2.x.
### Versionamento
**2.0**: Lançamento para usuários iniciais. Recursos principais disponíveis, mas pode não estar totalmente
pronto para produção.
**2.1 / 2.2**: Lançamento para produção. Estabilidade e completude validadas.
A flexibilidade em relação à maturidade é intencional - os usuários iniciais podem acessar novos
recursos antes que todos os recursos sejam aprimorados para produção.
## Objetivos
Os principais objetivos deste trabalho são permitir metadados sobre fatos/declarações:
**Informações temporais**: Associar fatos a metadados de tempo
Quando um fato foi considerado verdadeiro
Quando um fato se tornou verdadeiro
Quando um fato foi descoberto como falso
**Proveniência/Fontes**: Rastrear quais fontes suportam um fato
"Este fato foi suportado pela fonte X"
<<<<<<< HEAD
Vincular fatos aos documentos de origem
=======
Vincular fatos aos seus documentos de origem
>>>>>>> 82edf2d (New md files from RunPod)
**Veracidade/Confiança**: Registrar afirmações sobre a verdade
"A pessoa P afirmou que isso era verdade"
"A pessoa Q afirma que isso é falso"
Permitir pontuação de confiança e detecção de conflitos
**Hipótese**: A reificação (triplas RDF-star / citadas) é o mecanismo chave
para alcançar esses resultados, pois todos exigem fazer declarações sobre declarações.
## Contexto
Para expressar "o fato (Alice conhece Bob) foi descoberto em 2024-01-15" ou
"a fonte X suporta a afirmação (Y causa Z)", você precisa referenciar uma aresta
como algo sobre o qual você pode fazer declarações. Triplas padrão não suportam isso.
### Limitações Atuais
<<<<<<< HEAD
A classe `Value` atual em `trustgraph-base/trustgraph/schema/core/primitives.py`
=======
A classe `Value` em `trustgraph-base/trustgraph/schema/core/primitives.py`
>>>>>>> 82edf2d (New md files from RunPod)
pode representar:
Nós URI (`is_uri=True`)
Valores literais (`is_uri=False`)
O campo `type` existe, mas não é usado para representar tipos de dados XSD.
## Design Técnico
### Recursos RDF a serem Suportados
#### Recursos Principais (Relacionados aos Objetivos de Reificação)
Esses recursos estão diretamente relacionados aos objetivos de tempo, proveniência e veracidade:
1. **Triplas Citadas RDF 1.2 (RDF-star)**
Arestas que apontam para outras arestas
<<<<<<< HEAD
Uma Tripla pode aparecer como o sujeito ou objeto de outra Tripla
=======
Uma Tripla pode aparecer como o sujeito ou o objeto de outra Tripla
>>>>>>> 82edf2d (New md files from RunPod)
Permite declarações sobre declarações (reificação)
Mecanismo principal para anotar fatos individuais
2. **Conjunto de Dados RDF / Grafos Nomeados**
Suporte para vários grafos nomeados dentro de um conjunto de dados
Cada grafo identificado por um IRI
Passa de triplas (s, p, o) para quads (s, p, o, g)
Inclui um grafo padrão, mais zero ou mais grafos nomeados
O IRI do grafo pode ser um sujeito em declarações, por exemplo:
O IRI do grafo pode ser o sujeito em declarações, por exemplo:
```
<graph-source-A> <discoveredOn> "2024-01-15"
<graph-source-A> <hasVeracity> "high"
```
Nota: Os grafos nomeados são uma funcionalidade separada da reificação. Eles têm
usos além da anotação de declarações (particionamento, controle de acesso, organização de conjuntos de dados)
e devem ser tratados como uma capacidade distinta.
3. **Nós Anônimos** (Suporte Limitado)
Nós anônimos sem um URI global
Suportados para compatibilidade ao carregar dados RDF externos
**Status limitado**: Sem garantias sobre a identidade estável após o carregamento
Encontre-os por meio de consultas curinga (correspondência por conexões, não por ID)
Não é uma funcionalidade de primeira classe - não dependa de um tratamento preciso de nós anônimos
#### Correções Oportunas (Mudança Incompatível 2.0)
Esses recursos não estão diretamente relacionados aos objetivos da reificação, mas são
melhorias valiosas a serem incluídas ao mesmo tempo em que são feitas alterações incompatíveis:
4. **Tipos de Dados Literais**
Use corretamente o campo `type` para tipos de dados XSD
Exemplos: xsd:string, xsd:integer, xsd:dateTime, etc.
Corrige a limitação atual: não é possível representar datas ou inteiros corretamente
5. **Tags de Idioma**
Suporte para atributos de idioma em literais de string (@en, @fr, etc.)
Nota: Um literal tem uma tag de idioma OU um tipo de dados, não ambos
(exceto para rdf:langString)
Importante para casos de uso de IA/multilíngues
### Modelos de Dados
#### Termo (renomeado de Valor)
A classe `Value` será renomeada para `Term` para melhor refletir a terminologia RDF.
Esta renomeação tem dois propósitos:
1. Alinha a nomenclatura com os conceitos RDF (um "Termo" pode ser um IRI, literal ou nó anônimo.
nó, ou tripla entre aspas - não apenas um "valor").
2. Exige revisão de código na interface de alteração significativa - qualquer código que ainda
faça referência a `Value` estará visivelmente quebrado e precisará ser atualizado.
Um Termo pode representar:
**IRI/URI** - Um nó/recurso nomeado.
**Nó Anônimo** - Um nó anônimo com escopo local.
**Literal** - Um valor de dados com um dos seguintes:
Um tipo de dados (tipo XSD), OU
Uma etiqueta de idioma.
**Tripla Citada** - Uma tripla usada como um termo (RDF 1.2).
##### Abordagem Escolhida: Classe Única com Discriminador de Tipo
Os requisitos de serialização determinam a estrutura - um discriminador de tipo é necessário
<<<<<<< HEAD
no formato de transmissão, independentemente da representação em Python. Uma classe única com
=======
no formato de transmissão, independentemente da representação em Python. Uma única classe com
>>>>>>> 82edf2d (New md files from RunPod)
um campo de tipo é a opção mais adequada e está alinhada com o padrão atual `Value`.
Códigos de tipo de caractere único fornecem serialização compacta:
```python
from dataclasses import dataclass
# Term type constants
IRI = "i" # IRI/URI node
BLANK = "b" # Blank node
LITERAL = "l" # Literal value
TRIPLE = "t" # Quoted triple (RDF-star)
@dataclass
class Term:
type: str = "" # One of: IRI, BLANK, LITERAL, TRIPLE
# For IRI terms (type == IRI)
iri: str = ""
# For blank nodes (type == BLANK)
id: str = ""
# For literals (type == LITERAL)
value: str = ""
datatype: str = "" # XSD datatype URI (mutually exclusive with language)
language: str = "" # Language tag (mutually exclusive with datatype)
# For quoted triples (type == TRIPLE)
triple: "Triple | None" = None
```
Exemplos de uso:
```python
# IRI term
node = Term(type=IRI, iri="http://example.org/Alice")
# Literal with datatype
age = Term(type=LITERAL, value="42", datatype="xsd:integer")
# Literal with language tag
label = Term(type=LITERAL, value="Hello", language="en")
# Blank node
anon = Term(type=BLANK, id="_:b1")
# Quoted triple (statement about a statement)
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
**Opção B: União de classes especializadas** (`Term = IRI | BlankNode | Literal | QuotedTriple`)
Rejeitada: A serialização ainda precisaria de um discriminador de tipo, adicionando complexidade.
**Opção C: Classe base com subclasses**
Rejeitada: Mesmo problema de serialização, além de peculiaridades da herança de dataclasses.
#### Tripla / Quádrupla
A classe `Triple` ganha um campo de grafo opcional para se tornar uma quádrupla:
```python
@dataclass
class Triple:
s: Term | None = None # Subject
p: Term | None = None # Predicate
o: Term | None = None # Object
g: str | None = None # Graph name (IRI), None = default graph
```
Decisões de design:
**Nome do campo**: `g` para consistência com `s`, `p`, `o`
**Opcional**: `None` significa o grafo padrão (sem nome)
**Tipo**: String simples (IRI) em vez de Termo
Os nomes dos grafos são sempre IRIs
Nodos vazios como nomes de grafos são descartados (muito confusos)
Não há necessidade da totalidade do mecanismo de Termos
Nota: O nome da classe permanece `Triple` mesmo que tecnicamente seja um quad agora.
Isso evita mudanças e "tripla" ainda é a terminologia comum para a parte s/p/o.
O contexto do grafo é metadado sobre onde a tripla reside.
### Padrões de Consulta Candidatos
<<<<<<< HEAD
O mecanismo de consulta atual aceita combinações de termos S, P, O. Com triplas
entre aspas, uma tripla em si se torna um termo válido nessas posições. Abaixo estão
padrões de consulta candidatos que suportam os objetivos originais.
=======
O mecanismo de consulta atual aceita combinações de termos S, P, O. Com triplas entre aspas,
uma tripla se torna um termo válido nessas posições. Abaixo estão os padrões de consulta
candidatos que suportam os objetivos originais.
>>>>>>> 82edf2d (New md files from RunPod)
#### Semântica do Parâmetro do Grafo
Seguindo as convenções do SPARQL para compatibilidade com versões anteriores:
**`g` omitido / Nenhum**: Consulta apenas o grafo padrão
**`g` = IRI específico**: Consulta apenas aquele grafo nomeado
**`g` = curinga / `*`**: Consulta em todos os grafos (equivalente ao SPARQL
`GRAPH ?g { ... }`)
Isso mantém as consultas simples simples e torna as consultas de grafos nomeados opcionais.
Consultas entre grafos (g=curinga) são totalmente suportadas. O esquema do Cassandra
inclui tabelas dedicadas (SPOG, POSG, OSPG) onde g é uma coluna de agrupamento
em vez de uma chave de partição, permitindo consultas eficientes em todos os grafos.
#### Consultas Temporais
**Encontre todos os fatos descobertos após uma determinada data:**
```
S: ? # any quoted triple
P: <discoveredOn>
O: > "2024-01-15"^^xsd:date # date comparison
```
**Descubra quando um fato específico foi considerado verdadeiro:**
```
S: << <Alice> <knows> <Bob> >> # quoted triple as subject
P: <believedTrueFrom>
O: ? # returns the date
```
**Encontre fatos que se tornaram falsos:**
```
S: ? # any quoted triple
P: <discoveredFalseOn>
O: ? # has any value (exists)
```
#### Consultas de Proveniência
**Encontre todos os fatos suportados por uma fonte específica:**
```
S: ? # any quoted triple
P: <supportedBy>
O: <source:document-123>
```
**Descubra quais fontes sustentam um fato específico:**
```
S: << <DrugA> <treats> <DiseaseB> >> # quoted triple as subject
P: <supportedBy>
O: ? # returns source IRIs
```
#### Consultas de Veracidade
**Encontre as afirmações que uma pessoa marcou como verdadeiras:**
```
S: ? # any quoted triple
P: <assertedTrueBy>
O: <person:Alice>
```
**Encontre asserções conflitantes (mesmo fato, diferentes níveis de veracidade):**
```
# First query: facts asserted true
S: ?
P: <assertedTrueBy>
O: ?
# Second query: facts asserted false
S: ?
P: <assertedFalseBy>
O: ?
# Application logic: find intersection of subjects
```
**Encontre fatos com uma pontuação de confiança abaixo do limite:**
```
S: ? # any quoted triple
P: <trustScore>
O: < 0.5 # numeric comparison
```
### Arquitetura
Mudanças significativas necessárias em vários componentes:
#### Este Repositório (trustgraph)
**Primitivos de esquema** (`trustgraph-base/trustgraph/schema/core/primitives.py`)
Renomeação de Value → Term
Nova estrutura de Term com discriminador de tipo
Triple ganha campo `g` para contexto do grafo
**Tradutores de mensagens** (`trustgraph-base/trustgraph/messaging/translators/`)
Atualização para novas estruturas de Term/Triple
Serialização/desserialização para novos campos
**Componentes de gateway**
Lidar com novas estruturas de Term e quad
**Núcleos de conhecimento**
Mudanças no núcleo para suportar quads e reificação
**Gerenciador de conhecimento**
Mudanças de esquema se propagam aqui
**Camadas de armazenamento**
Cassandra: Redesenho do esquema (veja Detalhes de Implementação)
Outros backends: Adiado para fases posteriores
**Utilitários de linha de comando**
Atualização para novas estruturas de dados
**Documentação da API REST**
Atualizações da especificação OpenAPI
#### Repositórios Externos
**API Python** (este repositório)
Atualizações da biblioteca cliente para novas estruturas
**APIs TypeScript** (repositório separado)
Atualizações da biblioteca cliente
**Workbench** (repositório separado)
Mudanças significativas no gerenciamento de estado
### APIs
#### API REST
Documentado na especificação OpenAPI
Será necessário atualizar para novas estruturas de Term/Triple
Novos endpoints podem ser necessários para operações de contexto do grafo
#### API Python (este repositório)
Mudanças na biblioteca cliente para corresponder a novos primitivos
Mudanças significativas em Term (era Value) e Triple
#### API TypeScript (repositório separado)
Mudanças paralelas à API Python
Coordenação de lançamento separada
#### Workbench (repositório separado)
Mudanças significativas no gerenciamento de estado
Atualizações da interface do usuário para recursos de contexto do grafo
### Detalhes de Implementação
#### Implementação de Armazenamento em Fases
Existem vários backends de armazenamento de grafos (Cassandra, Neo4j, etc.). A implementação
seguirá em fases:
1. **Fase 1: Cassandra**
Começar com o armazenamento Cassandra próprio
<<<<<<< HEAD
O controle total sobre a camada de armazenamento permite iteração rápida
=======
Controle total sobre a camada de armazenamento permite iteração rápida
>>>>>>> 82edf2d (New md files from RunPod)
O esquema será redesenhado do zero para quads + reificação
Validar o modelo de dados e os padrões de consulta em relação a casos de uso reais
#### Design de Esquema do Cassandra
O Cassandra requer múltiplas tabelas para suportar diferentes padrões de acesso a consultas
(cada tabela consulta de forma eficiente pela sua chave de partição + colunas de agrupamento).
##### Padrões de Consulta
<<<<<<< HEAD
Com quads (g, s, p, o), cada posição pode ser especificada ou curinga, dando
=======
Com tuplas (g, s, p, o), cada posição pode ser especificada ou curinga, dando
>>>>>>> 82edf2d (New md files from RunPod)
16 padrões de consulta possíveis:
| # | g | s | p | o | Descrição |
|---|---|---|---|---|-------------|
<<<<<<< HEAD
| 1 | ? | ? | ? | ? | Todos os quads |
=======
| 1 | ? | ? | ? | ? | Todas as tuplas |
>>>>>>> 82edf2d (New md files from RunPod)
| 2 | ? | ? | ? | o | Por objeto |
| 3 | ? | ? | p | ? | Por predicado |
| 4 | ? | ? | p | o | Por predicado + objeto |
| 5 | ? | s | ? | ? | Por sujeito |
| 6 | ? | s | ? | o | Por sujeito + objeto |
| 7 | ? | s | p | ? | Por sujeito + predicado |
| 8 | ? | s | p | o | Tripla completa (quais grafos?) |
| 9 | g | ? | ? | ? | Por grafo |
| 10 | g | ? | ? | o | Por grafo + objeto |
| 11 | g | ? | p | ? | Por grafo + predicado |
| 12 | g | ? | p | o | Por grafo + predicado + objeto |
| 13 | g | s | ? | ? | Por grafo + sujeito |
| 14 | g | s | ? | o | Por grafo + sujeito + objeto |
| 15 | g | s | p | ? | Por grafo + sujeito + predicado |
<<<<<<< HEAD
| 16 | g | s | p | o | Quad exato |
##### Design de Tabela
Restrição do Cassandra: Você só pode consultar de forma eficiente pela chave de partição e, em seguida,
filtrar nas colunas de agrupamento da esquerda para a direita. Para consultas com curinga "g", "g" deve ser
uma coluna de agrupamento. Para consultas com "g" especificado, "g" na chave de partição é mais
=======
| 16 | g | s | p | o | Tupla exata |
##### Design da Tabela
Restrição do Cassandra: Você só pode consultar de forma eficiente pela chave de partição e, em seguida,
filtrar nas colunas de agrupamento da esquerda para a direita. Para consultas com curinga "g", "g" deve estar
em uma coluna de agrupamento. Para consultas com "g" especificado, "g" na chave de partição é mais
>>>>>>> 82edf2d (New md files from RunPod)
eficiente.
**Duas famílias de tabelas necessárias:**
<<<<<<< HEAD
**Família A: consultas com curinga "g"** (g em colunas de agrupamento)
=======
**Família A: Consultas com curinga "g"** (g em colunas de agrupamento)
>>>>>>> 82edf2d (New md files from RunPod)
| Tabela | Partição | Agrupamento | Suporta padrões |
|-------|-----------|------------|-------------------|
| SPOG | (user, collection, s) | p, o, g | 5, 7, 8 |
| POSG | (user, collection, p) | o, s, g | 3, 4 |
| OSPG | (user, collection, o) | s, p, g | 2, 6 |
<<<<<<< HEAD
**Família B: consultas com "g" especificado** (g na chave de partição)
=======
**Família B: Consultas com "g" especificado** (g na chave de partição)
>>>>>>> 82edf2d (New md files from RunPod)
| Tabela | Partição | Agrupamento | Suporta padrões |
|-------|-----------|------------|-------------------|
| GSPO | (user, collection, g, s) | p, o | 9, 13, 15, 16 |
| GPOS | (user, collection, g, p) | o, s | 11, 12 |
| GOSP | (user, collection, g, o) | s, p | 10, 14 |
**Tabela de coleção** (para iteração e exclusão em massa)
| Tabela | Partição | Agrupamento | Propósito |
|-------|-----------|------------|---------|
<<<<<<< HEAD
| COLL | (user, collection) | g, s, p, o | Enumerar todos os quads na coleção |
=======
| COLL | (user, collection) | g, s, p, o | Enumerar todas as tuplas na coleção |
>>>>>>> 82edf2d (New md files from RunPod)
##### Caminhos de Escrita e Exclusão
**Caminho de escrita**: Inserir em todas as 7 tabelas.
**Caminho de exclusão da coleção**:
1. Iterar na tabela COLL para `(user, collection)`
<<<<<<< HEAD
2. Para cada quad, excluir de todas as 6 tabelas de consulta
3. Excluir da tabela COLL (ou exclusão por intervalo)
**Caminho de exclusão de um único quad**: Excluir diretamente de todas as 7 tabelas.
##### Custo de Armazenamento
Cada quad é armazenado 7 vezes. Este é o custo da consulta flexível combinada
com exclusão eficiente da coleção.
##### Triplas Citadas no Armazenamento
O sujeito ou o objeto podem ser uma tripla em si. Opções:
=======
2. Para cada tupla, excluir de todas as 6 tabelas de consulta
3. Excluir da tabela COLL (ou exclusão por intervalo)
**Caminho de exclusão de uma única tupla**: Excluir diretamente de todas as 7 tabelas.
##### Custo de Armazenamento
Cada tupla é armazenada 7 vezes. Este é o custo da consulta flexível
combinado com a exclusão eficiente da coleção.
##### Triplas Citadas no Armazenamento
Sujeito ou objeto podem ser uma tripla em si. Opções:
>>>>>>> 82edf2d (New md files from RunPod)
**Opção A: Serializar triplas citadas para string canônica**
```
S: "<<http://ex/Alice|http://ex/knows|http://ex/Bob>>"
P: http://ex/discoveredOn
O: "2024-01-15"
G: null
```
Armazenar a tripla citada como uma string serializada nas colunas S ou O.
Consultar por correspondência exata na forma serializada.
Prós: Simples, se encaixa em padrões de índice existentes.
Contras: Não é possível consultar "encontrar triplas onde o predicado do sujeito citado é X".
**Opção B: IDs / Hashes de Triplas**
```
Triple table:
id: hash(s,p,o,g)
s, p, o, g: ...
Metadata table:
subject_triple_id: <hash>
p: http://ex/discoveredOn
o: "2024-01-15"
```
Atribua a cada tripla um ID (hash dos componentes)
As referências de metadados de reificação referenciam as triplas por ID
Prós: Separação limpa, pode indexar os IDs das triplas
Contras: Requer o cálculo/gerenciamento da identidade da tripla, pesquisas em duas fases
**Recomendação**: Comece com a Opção A (strings serializadas) para simplificar.
A Opção B pode ser necessária se forem necessários padrões de consulta avançados sobre triplas com aspas
componentes.
2. **Fase 2+: Outros Backends**
Neo4j e outros armazenamentos implementados em estágios subsequentes
As lições aprendidas com o Cassandra informam essas implementações
Esta abordagem reduz os riscos do projeto, validando em um backend totalmente controlado
antes de implementar em todos os armazenamentos.
<<<<<<< HEAD
#### Renomear Classe de Valor → Termo
=======
#### Renomeação de Valor → Termo
>>>>>>> 82edf2d (New md files from RunPod)
A classe `Value` será renomeada para `Term`. Isso afeta aproximadamente 78 arquivos em
todo o código-fonte. A renomeação funciona como um fator de força: qualquer código que ainda use
`Value` pode ser identificado imediatamente como precisando de revisão/atualização para compatibilidade com a versão 2.0
## Considerações de Segurança
Os grafos nomeados não são um recurso de segurança. Usuários e coleções permanecem
como os limites de segurança. Os grafos nomeados são puramente para organização de dados e
suporte de reificação.
## Considerações de Desempenho
Triplas com aspas adicionam profundidade de aninhamento - podem afetar o desempenho da consulta
Estratégias de indexação de grafos nomeados necessárias para consultas eficientes com escopo de grafo
O design do esquema do Cassandra precisará acomodar o armazenamento de quads de forma eficiente
### Limite do Armazenamento Vetorial
Os armazenamentos vetoriais sempre referenciam apenas IRIs:
Nunca arestas (triplas com aspas)
Nunca valores literais
Nunca nós vazios
<<<<<<< HEAD
Isso mantém o armazenamento vetorial simples - ele lida com a similaridade semântica de entidades nomeadas. A estrutura do grafo lida com relacionamentos, reificação e metadados.
=======
Isso mantém o armazenamento vetorial simples - ele lida com a similaridade semântica de entidades nomeadas.
A estrutura do grafo lida com relacionamentos, reificação e metadados.
>>>>>>> 82edf2d (New md files from RunPod)
Triplas com aspas e grafos nomeados não complicam as operações vetoriais.
## Estratégia de Teste
Use a estratégia de teste existente. Como esta é uma alteração disruptiva, concentre-se extensivamente no
conjunto de testes de ponta a ponta para validar que as novas estruturas funcionam corretamente em
todos os componentes.
<<<<<<< HEAD
=======
>>>>>>> 82edf2d (New md files from RunPod)
## Plano de Migração
A versão 2.0 é uma versão disruptiva; nenhuma compatibilidade com versões anteriores é necessária
Os dados existentes podem precisar ser migrados para um novo esquema (a ser definido com base no design final)
Considere ferramentas de migração para converter triplas existentes
## Perguntas Abertas
**Nós vazios**: Suporte limitado confirmado. Pode ser necessário decidir sobre
<<<<<<< HEAD
estratégia de skolemização (gerar IRIs na carga, ou preservar os IDs dos nós vazios).
=======
estratégia de skolemização (gerar IRIs na carga ou preservar os IDs dos nós vazios).
>>>>>>> 82edf2d (New md files from RunPod)
**Sintaxe de consulta**: Qual é a sintaxe concreta para especificar triplas com aspas
em consultas? É necessário definir a API de consulta.
~~**Vocabulário de predicados**~~: Resolvido. Qualquer predicado RDF válido é permitido,
incluindo vocabulários personalizados do usuário. Mínimas suposições sobre a validade do RDF.
Pouquíssimos valores fixos (por exemplo, `rdfs:label` usado em alguns lugares).
<<<<<<< HEAD
Estratégia: evite fixar qualquer coisa, a menos que seja absolutamente necessário.
~~**Impacto no armazenamento vetorial**~~: Resolvido. Os armazenamentos vetoriais sempre apontam para IRIs
apenas - nunca arestas, literais ou nós vazios. Triplas com aspas e
a reificação não afetam o armazenamento vetorial.
~~**Semântica do grafo nomeado**~~: Resolvido. As consultas padrão
para o grafo padrão (corresponde ao comportamento do SPARQL, compatível com versões anteriores). Parâmetro de grafo explícito
=======
Estratégia: evitar fixar qualquer coisa, a menos que seja absolutamente necessário.
~~**Impacto no armazenamento vetorial**~~: Resolvido. Os armazenamentos vetoriais sempre apontam para IRIs
apenas - nunca arestas, literais ou nós vazios. Triplas com aspas e
a reificação não afetam o armazenamento vetorial.
~~**Semântica do grafo nomeado**~~: Resolvido. As consultas usam o grafo padrão
(corresponde ao comportamento do SPARQL, compatível com versões anteriores). Parâmetro de grafo explícito
>>>>>>> 82edf2d (New md files from RunPod)
necessário para consultar grafos nomeados ou todos os grafos.
## Referências
[Conceitos RDF 1.2](https://www.w3.org/TR/rdf12-concepts/)
[RDF-star e SPARQL-star](https://w3c.github.io/rdf-star/)
[Conjunto de dados RDF](https://www.w3.org/TR/rdf11-concepts/#section-dataset)

View file

@ -0,0 +1,501 @@
---
layout: default
title: "Especificação Técnica da Consulta GraphQL"
parent: "Portuguese (Beta)"
---
# Especificação Técnica da 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.
## Visão Geral
Esta especificação descreve a implementação de uma interface de consulta GraphQL para o armazenamento de dados estruturados do TrustGraph no Apache Cassandra. Baseando-se nas capacidades de dados estruturados descritas na especificação structured-data.md, este documento detalha como as consultas GraphQL serão executadas contra tabelas do Cassandra contendo objetos estruturados extraídos e ingeridos.
O serviço de consulta GraphQL fornecerá uma interface flexível e segura para consultar dados estruturados armazenados no Cassandra. Ele se adaptará dinamicamente às alterações de esquema, suportará consultas complexas, incluindo relacionamentos entre objetos, e se integrará perfeitamente à arquitetura existente baseada em mensagens do TrustGraph.
## Objetivos
**Suporte Dinâmico de Esquema**: Adaptar-se automaticamente às alterações de esquema na configuração sem reinicializações do serviço.
**Conformidade com os Padrões GraphQL**: Fornecer uma interface GraphQL padrão compatível com as ferramentas e clientes GraphQL existentes.
**Consultas Eficientes do Cassandra**: Traduzir consultas GraphQL em consultas CQL eficientes do Cassandra, respeitando as chaves de partição e os índices.
**Resolução de Relacionamentos**: Suportar resolvedores de campos GraphQL para relacionamentos entre diferentes tipos de objetos.
**Segurança de Tipo**: Garantir a execução de consultas e a geração de respostas com segurança de tipo, com base nas definições do esquema.
**Desempenho Escalável**: Lidar com consultas concorrentes de forma eficiente, com um pool de conexões adequado e otimização de consultas.
<<<<<<< HEAD
**Integração de Solicitação/Resposta**: Manter a compatibilidade com o padrão de solicitação/resposta baseado no Pulsar do TrustGraph.
=======
**Integração de Solicitação/Resposta**: Manter a compatibilidade com o padrão de solicitação/resposta baseado em Pulsar do TrustGraph.
>>>>>>> 82edf2d (New md files from RunPod)
**Tratamento de Erros**: Fornecer relatórios de erros abrangentes para incompatibilidades de esquema, erros de consulta e problemas de validação de dados.
## Contexto
<<<<<<< HEAD
A implementação do armazenamento de dados estruturados (trustgraph-flow/trustgraph/storage/objects/cassandra/) grava objetos em tabelas do Cassandra com base em definições de esquema armazenadas no sistema de configuração do TrustGraph. Essas tabelas usam uma estrutura de chave de partição composta com chaves primárias definidas por coleção e esquema, permitindo consultas eficientes dentro das coleções.
=======
A implementação de armazenamento de dados estruturados (trustgraph-flow/trustgraph/storage/objects/cassandra/) grava objetos em tabelas do Cassandra com base em definições de esquema armazenadas no sistema de configuração do TrustGraph. Essas tabelas usam uma estrutura de chave de partição composta com chaves primárias definidas por coleção, permitindo consultas eficientes dentro de coleções.
>>>>>>> 82edf2d (New md files from RunPod)
Limitações atuais que esta especificação aborda:
Não há interface de consulta para os dados estruturados armazenados no Cassandra.
Impossibilidade de aproveitar os poderosos recursos de consulta do GraphQL para dados estruturados.
Falta de suporte para a travessia de relacionamentos entre objetos relacionados.
Falta de uma linguagem de consulta padronizada para acesso a dados estruturados.
O serviço de consulta GraphQL preencherá essas lacunas fornecendo:
Uma interface GraphQL padrão para consultar tabelas do Cassandra.
Geração dinâmica de esquemas GraphQL a partir da configuração do TrustGraph.
Tradução eficiente de consultas GraphQL para CQL do Cassandra.
Suporte para resolução de relacionamentos por meio de resolvedores de campos.
## Design Técnico
### Arquitetura
O serviço de consulta GraphQL será implementado como um novo processador de fluxo do TrustGraph, seguindo padrões estabelecidos:
**Localização do Módulo**: `trustgraph-flow/trustgraph/query/objects/cassandra/`
**Componentes Principais**:
<<<<<<< HEAD
1. **Processador do Serviço de Consulta GraphQL**
Estende a classe base FlowProcessor.
Implementa um padrão de solicitação/resposta semelhante aos serviços de consulta existentes.
=======
1. **Processador de Serviço de Consulta GraphQL**
Estende a classe base FlowProcessor.
Implementa um padrão de solicitação/resposta semelhante a outros serviços de consulta existentes.
>>>>>>> 82edf2d (New md files from RunPod)
Monitora a configuração para atualizações de esquema.
Mantém o esquema GraphQL sincronizado com a configuração.
2. **Gerador de Esquema Dinâmico**
Converte definições de RowSchema do TrustGraph em tipos GraphQL.
<<<<<<< HEAD
Cria tipos de objetos GraphQL com definições de campo adequadas.
=======
Cria tipos de objetos GraphQL com definições de campo apropriadas.
>>>>>>> 82edf2d (New md files from RunPod)
Gera o tipo de consulta raiz com resolvedores baseados em coleções.
Atualiza o esquema GraphQL quando a configuração é alterada.
3. **Executor de Consulta**
Analisa consultas GraphQL recebidas usando a biblioteca Strawberry.
Valida as consultas em relação ao esquema atual.
Executa as consultas e retorna respostas estruturadas.
Lida com erros de forma elegante com mensagens de erro detalhadas.
4. **Tradutor de Consulta do Cassandra**
Converte seleções GraphQL em consultas CQL.
Otimiza as consultas com base nos índices e chaves de partição disponíveis.
Lida com filtragem, paginação e classificação.
Gerencia o pool de conexões e o ciclo de vida da sessão.
5. **Resolvedor de Relacionamentos**
Implementa resolvedores de campos para relacionamentos entre objetos.
Realiza carregamento em lote eficiente para evitar consultas N+1.
Armazena em cache os relacionamentos resolvidos dentro do contexto da solicitação.
Suporta a travessia de relacionamentos tanto para frente quanto para trás.
### Monitoramento do Esquema de Configuração
O serviço registrará um manipulador de configuração para receber atualizações de esquema:
```python
self.register_config_handler(self.on_schema_config)
```
Quando os esquemas mudam:
1. Analisar as novas definições de esquema a partir da configuração
2. Regenerar os tipos e resolvedores GraphQL
3. Atualizar o esquema executável
4. Limpar quaisquer caches dependentes do esquema
### Geração de Esquema GraphQL
Para cada RowSchema na configuração, gerar:
1. **Tipo de Objeto GraphQL**:
Mapear tipos de campo (string → String, integer → Int, float → Float, boolean → Boolean)
Marcar campos obrigatórios como não nulos no GraphQL
Adicionar descrições de campo do esquema
2. **Campos de Consulta Raiz**:
Consulta de coleção (por exemplo, `customers`, `transactions`)
Argumentos de filtragem com base em campos indexados
Suporte de paginação (limite, deslocamento)
Opções de ordenação para campos classificáveis
3. **Campos de Relacionamento**:
Identificar relacionamentos de chave estrangeira do esquema
Criar resolvedores de campo para objetos relacionados
Suportar relacionamentos de objeto único e de lista
### Fluxo de Execução da Consulta
1. **Recepção da Requisição**:
Receber ObjectsQueryRequest do Pulsar
Extrair a string de consulta GraphQL e as variáveis
Identificar o contexto de usuário e coleção
2. **Validação da Consulta**:
Analisar a consulta GraphQL usando Strawberry
Validar em relação ao esquema atual
Verificar as seleções de campo e os tipos de argumento
3. **Geração de CQL**:
Analisar as seleções GraphQL
Construir a consulta CQL com as cláusulas WHERE apropriadas
Incluir a coleção na chave de partição
Aplicar filtros com base nos argumentos GraphQL
4. **Execução da Consulta**:
Executar a consulta CQL contra o Cassandra
Mapear os resultados para a estrutura de resposta GraphQL
Resolver quaisquer campos de relacionamento
Formatar a resposta de acordo com a especificação GraphQL
5. **Entrega da Resposta**:
Criar uma ObjectsQueryResponse com os resultados
Incluir quaisquer erros de execução
Enviar a resposta via Pulsar com o ID de correlação
### Modelos de Dados
<<<<<<< HEAD
> **Nota**: Um esquema StructuredQueryRequest/Response existente existe em `trustgraph-base/trustgraph/schema/services/structured_query.py`. No entanto, ele carece de campos críticos (usuário, coleção) e usa tipos subótimos. Os esquemas abaixo representam a evolução recomendada, que deve substituir os esquemas existentes ou ser criada como novos tipos ObjectsQueryRequest/Response.
=======
> **Observação**: Um esquema StructuredQueryRequest/Response existente existe em `trustgraph-base/trustgraph/schema/services/structured_query.py`. No entanto, ele carece de campos críticos (usuário, coleção) e usa tipos subótimos. Os esquemas abaixo representam a evolução recomendada, que deve substituir os esquemas existentes ou ser criada como novos tipos ObjectsQueryRequest/Response.
>>>>>>> 82edf2d (New md files from RunPod)
#### Esquema de Requisição (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
```
**Justificativa para as alterações em relação ao StructuredQueryRequest existente:**
Adicionados os campos `user` e `collection` para corresponder ao padrão de outros serviços de consulta.
Esses campos são essenciais para identificar o keyspace e a coleção do Cassandra.
As variáveis permanecem como Map(String()) por enquanto, mas idealmente deveriam suportar todos os tipos JSON.
#### Esquema de Resposta (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.)
```
**Justificativa para as alterações em relação à StructuredQueryResponse existente:**
Distingue entre erros do sistema (`error`) e erros do GraphQL (`errors`)
Utiliza objetos GraphQLError estruturados em vez de um array de strings
Adiciona o campo `extensions` para conformidade com a especificação do GraphQL
Mantém os dados como uma string JSON para compatibilidade, embora os tipos nativos seriam preferíveis
### Otimização de Consultas Cassandra
O serviço otimizará as consultas do Cassandra, através de:
1. **Respeitando as Chaves de Partição:**
Inclua sempre a coleção nas consultas
Utilize as chaves primárias definidas no esquema de forma eficiente
Evite varreduras completas da tabela
2. **Aproveitando os Índices:**
Utilize índices secundários para filtragem
Combine vários filtros sempre que possível
Avise quando as consultas podem ser ineficientes
3. **Carregamento em Lote:**
Colete consultas de relacionamento
Execute em lotes para reduzir o número de viagens de ida e volta
Armazene em cache os resultados dentro do contexto da requisição
4. **Gerenciamento de Conexões:**
Mantenha sessões persistentes do Cassandra
Utilize um pool de conexões
Lide com a reconexão em caso de falhas
### Exemplos de Consultas GraphQL
#### Consulta Simples de Coleção
```graphql
{
customers(status: "active") {
customer_id
name
email
registration_date
}
}
```
#### Consulta com Relacionamentos
```graphql
{
orders(order_date_gt: "2024-01-01") {
order_id
total_amount
customer {
name
email
}
items {
product_name
quantity
price
}
}
}
```
#### Consulta Paginação
```graphql
{
products(limit: 20, offset: 40) {
product_id
name
price
category
}
}
```
### Dependências de Implementação
**Strawberry GraphQL**: Para definição de esquema GraphQL e execução de consultas.
**Cassandra Driver**: Para conectividade com o banco de dados (já utilizado no módulo de armazenamento).
**TrustGraph Base**: Para FlowProcessor e definições de esquema.
<<<<<<< HEAD
**Configuration System**: Para monitoramento e atualizações de esquema.
### Interface de Linha de Comando
O serviço fornecerá um comando de CLI: `kg-query-objects-graphql-cassandra`
=======
**Sistema de Configuração**: Para monitoramento e atualizações de esquema.
### Interface de Linha de Comando
O serviço fornecerá um comando de interface de linha de comando: `kg-query-objects-graphql-cassandra`
>>>>>>> 82edf2d (New md files from RunPod)
Argumentos:
`--cassandra-host`: Ponto de contato do cluster Cassandra.
`--cassandra-username`: Nome de usuário de autenticação.
`--cassandra-password`: Senha de autenticação.
`--config-type`: Tipo de configuração para esquemas (padrão: "schema").
Argumentos padrão do FlowProcessor (configuração do Pulsar, etc.).
## Integração de API
### Tópicos Pulsar
**Tópico de Entrada**: `objects-graphql-query-request`
Esquema: ObjectsQueryRequest
Recebe consultas GraphQL de serviços de gateway.
**Tópico de Saída**: `objects-graphql-query-response`
Esquema: ObjectsQueryResponse
Retorna resultados de consulta e erros.
### Integração de Gateway
<<<<<<< HEAD
O gateway e o gateway reverso precisarão de endpoints para:
=======
O gateway e o reverse-gateway precisarão de endpoints para:
>>>>>>> 82edf2d (New md files from RunPod)
1. Aceitar consultas GraphQL de clientes.
2. Encaminhar para o serviço de consulta via Pulsar.
3. Retornar respostas aos clientes.
4. Suportar consultas de introspecção GraphQL.
### Integração da Ferramenta de Agente
Uma nova classe de ferramenta de agente permitirá:
Geração de consultas GraphQL a partir de linguagem natural.
Execução direta de consultas GraphQL.
Interpretação e formatação de resultados.
Integração com fluxos de decisão do agente.
## Considerações de Segurança
<<<<<<< HEAD
**Limitação de Profundidade da Consulta**: Prevenir consultas profundamente aninhadas que possam causar problemas de desempenho.
**Análise de Complexidade da Consulta**: Limitar a complexidade da consulta para evitar o esgotamento de recursos.
**Permissões de Nível de Campo**: Suporte futuro para controle de acesso baseado em funções de usuário.
**Sanitização de Entrada**: Validar e sanitizar todas as entradas de consulta para prevenir ataques de injeção.
**Limitação de Taxa**: Implementar a limitação de taxa de consulta por usuário/coleção.
=======
**Limitação da Profundidade da Consulta**: Prevenir consultas profundamente aninhadas que possam causar problemas de desempenho.
**Análise de Complexidade da Consulta**: Limitar a complexidade da consulta para evitar o esgotamento de recursos.
**Permissões em Nível de Campo**: Suporte futuro para controle de acesso baseado em funções de usuário.
**Sanitização de Entrada**: Validar e sanitizar todas as entradas de consulta para prevenir ataques de injeção.
**Limitação de Taxa**: Implementar a limitação de taxa de consultas por usuário/coleção.
>>>>>>> 82edf2d (New md files from RunPod)
## Considerações de Desempenho
**Planejamento de Consulta**: Analisar consultas antes da execução para otimizar a geração de CQL.
**Cache de Resultados**: Considerar o cache de dados acessados com frequência no nível do resolvedor de campo.
<<<<<<< HEAD
**Pool de Conexões**: Manter pools de conexão eficientes para o Cassandra.
**Operações em Lote**: Combinar várias consultas sempre que possível para reduzir a latência.
**Monitoramento**: Rastrear métricas de desempenho da consulta para otimização.
=======
**Pool de Conexões**: Manter pools de conexões eficientes para o Cassandra.
**Operações em Lote**: Combinar várias consultas sempre que possível para reduzir a latência.
**Monitoramento**: Acompanhar métricas de desempenho de consultas para otimização.
>>>>>>> 82edf2d (New md files from RunPod)
## Estratégia de Teste
### Testes Unitários
<<<<<<< HEAD
Geração de esquema a partir de definições de RowSchema.
Análise e validação de consultas GraphQL.
Lógica de geração de consultas CQL.
Implementações de resolvedores de campo.
### Testes de Contrato
Conformidade do contrato de mensagem Pulsar.
Validade do esquema GraphQL.
Verificação do formato da resposta.
Validação da estrutura de erro.
### Testes de Integração
Execução de consulta de ponta a ponta contra uma instância de teste do Cassandra.
Tratamento de atualização de esquema.
Resolução de relacionamento.
Paginação e filtragem.
Cenários de erro.
### Testes de Desempenho
Taxa de transferência de consultas sob carga.
Tempo de resposta para várias complexidades de consulta.
Uso de memória com grandes conjuntos de resultados.
Eficiência do pool de conexões.
=======
Geração de esquema a partir de definições RowSchema
Análise e validação de consultas GraphQL
Lógica de geração de consultas CQL
Implementações de resolvedores de campos
### Testes de Contrato
Conformidade com o contrato de mensagens Pulsar
Validade do esquema GraphQL
Verificação do formato da resposta
Validação da estrutura de erros
### Testes de Integração
Execução de consulta ponta a ponta contra uma instância de teste do Cassandra
Tratamento de atualizações de esquema
Resolução de relacionamentos
Paginação e filtragem
Cenários de erro
### Testes de Desempenho
Taxa de transferência de consultas sob carga
Tempo de resposta para diferentes complexidades de consulta
Uso de memória com grandes conjuntos de resultados
Eficiência do pool de conexões
>>>>>>> 82edf2d (New md files from RunPod)
## Plano de Migração
Não é necessária migração, pois esta é uma nova funcionalidade. O serviço irá:
<<<<<<< HEAD
1. Ler esquemas existentes da configuração.
2. Conectar-se às tabelas Cassandra existentes criadas pelo módulo de armazenamento.
3. Começar a aceitar consultas imediatamente após a implantação.
=======
1. Ler esquemas existentes da configuração
2. Conectar-se às tabelas Cassandra existentes criadas pelo módulo de armazenamento
3. Começar a aceitar consultas imediatamente após a implantação
## Cronograma
Semana 1-2: Implementação do serviço principal e geração de esquema
Semana 3: Execução de consultas e tradução CQL
Semana 4: Resolução de relacionamentos e otimização
Semana 5: Testes e ajuste de desempenho
Semana 6: Integração com o gateway e documentação
>>>>>>> 82edf2d (New md files from RunPod)
## Perguntas Abertas
1. **Evolução do Esquema**: Como o serviço deve lidar com consultas durante as transições de esquema?
<<<<<<< HEAD
Opção: Enfileirar consultas durante as atualizações de esquema.
Opção: Suportar várias versões de esquema simultaneamente.
2. **Estratégia de Cache**: Os resultados da consulta devem ser armazenados em cache?
Considerar: Expiração baseada em tempo.
Considerar: Invalidação baseada em eventos.
3. **Suporte à Federação**: O serviço deve suportar a federação GraphQL para combinar com outras fontes de dados?
Permitiria consultas unificadas em dados estruturados e de grafo.
4. **Suporte a Assinaturas**: O serviço deve suportar assinaturas GraphQL para atualizações em tempo real?
Requereria suporte WebSocket no gateway.
5. **Escalares Personalizados**: O serviço deve suportar tipos escalares personalizados para tipos de dados específicos do domínio?
Exemplos: DateTime, UUID, campos JSON.
## Referências
Especificação de Dados Estruturados: ⟦CODE_0⟧
Documentação do Strawberry GraphQL: ⟦URL_0⟧
Especificação GraphQL: ⟦URL_0⟧
Referência CQL do Apache Cassandra: ⟦URL_0⟧
Documentação do Flow Processor TrustGraph: Documentação interna.
=======
Opção: Enfileirar consultas durante as atualizações de esquema
Opção: Suportar múltiplas versões de esquema simultaneamente
2. **Estratégia de Cache**: Os resultados das consultas devem ser armazenados em cache?
Considerar: Expiração baseada em tempo
Considerar: Invalidação baseada em eventos
3. **Suporte à Federação**: O serviço deve suportar a federação GraphQL para combinar com outras fontes de dados?
Permitiria consultas unificadas em dados estruturados e de grafo
4. **Suporte a Assinaturas**: O serviço deve suportar assinaturas GraphQL para atualizações em tempo real?
Requereria suporte WebSocket no gateway
5. **Escalares Personalizados**: O serviço deve suportar tipos escalares personalizados para tipos de dados específicos do domínio?
Exemplos: DateTime, UUID, campos JSON
>>>>>>> 82edf2d (New md files from RunPod)
## Referências
Especificação Técnica de Dados Estruturados: `docs/tech-specs/structured-data.md`
<<<<<<< HEAD
Documentação do Strawberry GraphQL: https://strawberry.rocks/
=======
Documentação do GraphQL Strawberry: https://strawberry.rocks/
>>>>>>> 82edf2d (New md files from RunPod)
Especificação do GraphQL: https://spec.graphql.org/
Referência CQL do Apache Cassandra: https://cassandra.apache.org/doc/stable/cassandra/cql/
Documentação do Processador de Fluxo TrustGraph: Documentação interna

View file

@ -0,0 +1,758 @@
---
layout: default
title: "Especificação Técnica de Otimização de Desempenho do GraphRAG"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Otimização de Desempenho do 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.
## Visão Geral
Esta especificação descreve otimizações abrangentes de desempenho para o algoritmo GraphRAG (Graph Retrieval-Augmented Generation) no TrustGraph. A implementação atual sofre de gargalos de desempenho significativos que limitam a escalabilidade e os tempos de resposta. Esta especificação aborda quatro áreas principais de otimização:
1. **Otimização de Traversal de Grafos**: Eliminar consultas ineficientes ao banco de dados e implementar exploração de grafos em lote.
2. **Otimização de Resolução de Rótulos**: Substituir a busca sequencial de rótulos por operações paralelas/em lote.
3. **Aprimoramento da Estratégia de Cache**: Implementar um cache inteligente com remoção LRU (Least Recently Used) e pré-busca.
<<<<<<< HEAD
4. **Otimização de Consultas**: Adicionar memorização de resultados e cache de incorporações para melhorar os tempos de resposta.
=======
4. **Otimização de Consulta**: Adicionar memorização de resultados e cache de embeddings para melhorar os tempos de resposta.
>>>>>>> 82edf2d (New md files from RunPod)
## Objetivos
**Reduzir o Volume de Consultas ao Banco de Dados**: Alcançar uma redução de 50-80% no volume total de consultas ao banco de dados por meio de loteamento e cache.
**Melhorar os Tempos de Resposta**: Almejar uma construção de subgrafo 3-5 vezes mais rápida e uma resolução de rótulos 2-3 vezes mais rápida.
**Aprimorar a Escalabilidade**: Suportar grafos de conhecimento maiores com melhor gerenciamento de memória.
**Manter a Precisão**: Preservar a funcionalidade e a qualidade dos resultados do GraphRAG existentes.
**Habilitar a Concorrência**: Melhorar as capacidades de processamento paralelo para vários запросов simultâneos.
**Reduzir a Pegada de Memória**: Implementar estruturas de dados e gerenciamento de memória eficientes.
**Adicionar Observabilidade**: Incluir métricas de desempenho e capacidades de monitoramento.
<<<<<<< HEAD
**Garantir a Confiabilidade**: Adicionar tratamento de erros adequado e mecanismos de tempo limite.
=======
**Garantir a Confiabilidade**: Adicionar tratamento de erros adequado e mecanismos de timeout.
>>>>>>> 82edf2d (New md files from RunPod)
## Contexto
A implementação atual do GraphRAG em `trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py` apresenta vários problemas de desempenho críticos que afetam severamente a escalabilidade do sistema:
### Problemas de Desempenho Atuais
**1. Traversal de Grafos Ineficiente (função `follow_edges`, linhas 79-127)**
Realiza 3 consultas separadas ao banco de dados por entidade por nível de profundidade.
Padrão de consulta: consultas baseadas em sujeito, baseadas em predicado e baseadas em objeto para cada entidade.
Sem loteamento: Cada consulta processa apenas uma entidade por vez.
Sem detecção de ciclo: Pode revisitar os mesmos nós várias vezes.
A implementação recursiva sem memorização leva a uma complexidade exponencial.
Complexidade de tempo: O(entidades × max_path_length × triple_limit³)
**2. Resolução Sequencial de Rótulos (função `get_labelgraph`, linhas 144-171)**
Processa cada componente de tripla (sujeito, predicado, objeto) sequencialmente.
Cada chamada de `maybe_label` pode acionar uma consulta ao banco de dados.
Sem execução paralela ou loteamento de consultas de rótulos.
Resulta em até 3 × subgraph_size chamadas individuais ao banco de dados.
**3. Estratégia de Cache Primitiva (função `maybe_label`, linhas 62-77)**
Cache de dicionário simples sem limites de tamanho ou TTL (Time To Live).
A ausência de uma política de remoção de cache leva a um crescimento de memória ilimitado.
Falhas de cache acionam consultas individuais ao banco de dados.
Sem pré-busca ou aquecimento inteligente do cache.
**4. Padrões de Consulta Subótimos**
<<<<<<< HEAD
Consultas de similaridade de vetores de entidade não são armazenadas em cache entre запросов semelhantes.
=======
Consultas de similaridade de vetores de entidades não são armazenadas em cache entre запросов semelhantes.
>>>>>>> 82edf2d (New md files from RunPod)
Sem memorização de resultados para padrões de consulta repetidos.
Otimização de consulta ausente para padrões de acesso comuns.
**5. Problemas Críticos de Tempo de Vida de Objetos (`rag.py:96-102`)**
**Objeto GraphRag recriado por запросом**: Uma nova instância é criada para cada consulta, perdendo todos os benefícios do cache.
**Objeto de consulta com vida extremamente curta**: Criado e destruído dentro da execução de uma única consulta (linhas 201-207).
**Cache de rótulos redefinido por запросом**: O aquecimento do cache e o conhecimento acumulado são perdidos entre запросов.
**Sobrecarga de recriação do cliente**: Clientes de banco de dados podem ser reestabelecidos para cada запросом.
**Sem otimização entre запросов**: Não é possível se beneficiar de padrões de consulta ou compartilhamento de resultados.
### Análise de Impacto no Desempenho
Cenário de pior caso atual para uma consulta típica:
**Recuperação de Entidade**: 1 consulta de similaridade de vetor.
**Traversal de Grafos**: entidades × max_path_length × 3 × consultas de triplas.
**Resolução de Rótulos**: subgraph_size × 3 consultas individuais de rótulos.
Para parâmetros padrão (50 entidades, comprimento do caminho 2, limite de 30 triplas, tamanho do subgrafo de 150):
<<<<<<< HEAD
**Consultas mínimas**: 1 + (50 × 2 × 3 × 30) + (150 × 3) = **9.451 consultas ao banco de dados**
**Tempo de resposta**: 15-30 segundos para grafos de tamanho moderado
**Uso de memória**: Crescimento ilimitado do cache ao longo do tempo
**Eficiência do cache**: 0% - os caches são reiniciados em cada solicitação
**Sobrecarga de criação de objetos**: Objetos GraphRag + Query criados/destruídos por solicitação
=======
**Número de consultas:** 1 + (50 × 2 × 3 × 30) + (150 × 3) = **9.451 consultas ao banco de dados**
**Tempo de resposta:** 15-30 segundos para grafos de tamanho moderado
**Uso de memória:** Crescimento ilimitado do cache ao longo do tempo
**Eficiência do cache:** 0% - os caches são reiniciados em cada solicitação
**Sobrecarga de criação de objetos:** Objetos GraphRag + Query criados/destruídos por solicitação
>>>>>>> 82edf2d (New md files from RunPod)
Esta especificação aborda essas lacunas implementando consultas em lote, cache inteligente e processamento paralelo. Ao otimizar padrões de consulta e acesso a dados, o TrustGraph pode:
Suportar grafos de conhecimento em escala empresarial com milhões de entidades
Fornecer tempos de resposta de menos de um segundo para consultas típicas
Lidar com centenas de solicitações GraphRAG simultâneas
Escalar de forma eficiente com o tamanho e a complexidade do grafo
## Design Técnico
### Arquitetura
A otimização de desempenho do GraphRAG requer os seguintes componentes técnicos:
#### 1. **Refatoração Arquitetural do Ciclo de Vida dos Objetos**
<<<<<<< HEAD
**Tornar o GraphRag de longa duração**: Mover a instância GraphRag para o nível do Processador para persistência entre solicitações
**Preservar caches**: Manter o cache de rótulos, o cache de incorporações e o cache de resultados de consulta entre solicitações
**Otimizar o objeto Query**: Refatorar o Query como um contexto de execução leve, não como um contêiner de dados
**Persistência de conexão**: Manter as conexões do cliente do banco de dados entre solicitações
=======
**Tornar o GraphRag de longa duração:** Mover a instância GraphRag para o nível do Processador para persistência entre solicitações
**Preservar caches:** Manter o cache de rótulos, o cache de incorporações e o cache de resultados de consulta entre solicitações
**Otimizar o objeto Query:** Refatorar o Query como um contexto de execução leve, não como um contêiner de dados
**Persistência de conexão:** Manter as conexões do cliente do banco de dados entre solicitações
>>>>>>> 82edf2d (New md files from RunPod)
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` (modificado)
#### 2. **Motor de Traversal de Grafos Otimizado**
Substituir a recursão `follow_edges` por busca em largura iterativa
Implementar o processamento em lote de entidades em cada nível de traversal
Adicionar detecção de ciclo usando o rastreamento de nós visitados
Incluir a terminação antecipada quando os limites são atingidos
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/optimized_traversal.py`
#### 3. **Sistema de Resolução de Rótulos Paralelo**
Agrupar consultas de rótulos para várias entidades simultaneamente
Implementar padrões async/await para acesso concorrente ao banco de dados
Adicionar pré-busca inteligente para padrões de rótulos comuns
Incluir estratégias de aquecimento de cache de rótulos
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/label_resolver.py`
#### 4. **Camada de Cache Conservadora de Rótulos**
Cache LRU com TTL curto apenas para rótulos (5 minutos) para equilibrar desempenho e consistência
Monitoramento de métricas e taxa de acerto do cache
<<<<<<< HEAD
**Sem cache de incorporações**: Já armazenado em cache por consulta, sem benefício entre consultas
**Sem cache de resultados de consulta**: Devido a preocupações com a consistência da mutação do grafo
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/cache_manager.py`
#### 5. **Framework de Otimização de Consulta**
=======
**Sem cache de incorporações:** Já armazenado em cache por consulta, sem benefício entre consultas
**Sem cache de resultados de consulta:** Devido a preocupações com a consistência da mutação do grafo
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/cache_manager.py`
#### 5. **Framework de Otimização de Consultas**
>>>>>>> 82edf2d (New md files from RunPod)
Análise e sugestões de otimização de padrões de consulta
Coordenador de consultas em lote para acesso ao banco de dados
Pool de conexões e gerenciamento de tempo limite de consulta
Monitoramento de desempenho e coleta de métricas
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/query_optimizer.py`
### Modelos de Dados
#### Estado Otimizado de Traversal de Grafos
O motor de traversal mantém o estado para evitar operações redundantes:
```python
@dataclass
class TraversalState:
visited_entities: Set[str]
current_level_entities: Set[str]
next_level_entities: Set[str]
subgraph: Set[Tuple[str, str, str]]
depth: int
query_batch: List[TripleQuery]
```
Esta abordagem permite:
Detecção eficiente de ciclos através do rastreamento de entidades visitadas.
Preparação de consultas em lote em cada nível de travessia.
Gerenciamento de estado com eficiência de memória.
Término antecipado quando os limites de tamanho são atingidos.
#### Estrutura de Cache Aprimorada
```python
@dataclass
class CacheEntry:
value: Any
timestamp: float
access_count: int
ttl: Optional[float]
class CacheManager:
label_cache: LRUCache[str, CacheEntry]
embedding_cache: LRUCache[str, CacheEntry]
query_result_cache: LRUCache[str, CacheEntry]
cache_stats: CacheStatistics
```
#### Estruturas de Consulta em Lote
```python
@dataclass
class BatchTripleQuery:
entities: List[str]
query_type: QueryType # SUBJECT, PREDICATE, OBJECT
limit_per_entity: int
@dataclass
class BatchLabelQuery:
entities: List[str]
predicate: str = LABEL
```
### APIs
#### Novas APIs:
**API de GraphTraversal**
```python
async def optimized_follow_edges_batch(
entities: List[str],
max_depth: int,
triple_limit: int,
max_subgraph_size: int
) -> Set[Tuple[str, str, str]]
```
**API de Resolução de Rótulos em Lote**
```python
async def resolve_labels_batch(
entities: List[str],
cache_manager: CacheManager
) -> Dict[str, str]
```
**API de Gerenciamento de Cache**
```python
class CacheManager:
async def get_or_fetch_label(self, entity: str) -> str
async def get_or_fetch_embeddings(self, query: str) -> List[float]
async def cache_query_result(self, query_hash: str, result: Any, ttl: int)
def get_cache_statistics(self) -> CacheStatistics
```
#### APIs Modificados:
**GraphRag.query()** - Aprimorado com otimizações de desempenho:
<<<<<<< HEAD
Adicionado parâmetro `cache_manager` para controle de cache.
Incluído valor de retorno `performance_metrics`.
Adicionado parâmetro `query_timeout` para confiabilidade.
=======
Adicionado o parâmetro `cache_manager` para controle de cache.
Incluído o valor de retorno `performance_metrics`.
Adicionado o parâmetro `query_timeout` para confiabilidade.
>>>>>>> 82edf2d (New md files from RunPod)
**Classe Query** - Refatorada para processamento em lote:
Substituição do processamento individual de entidades por operações em lote.
Adicionados gerenciadores de contexto assíncronos para limpeza de recursos.
Incluídas funções de retorno de progresso para operações de longa duração.
### Detalhes da Implementação
#### Fase 0: Refatoração Crítica do Ciclo de Vida da Arquitetura
**Implementação Atualmente Problemática:**
```python
# INEFFICIENT: GraphRag recreated every request
class Processor(FlowProcessor):
async def on_request(self, msg, consumer, flow):
# PROBLEM: New GraphRag instance per request!
self.rag = GraphRag(
embeddings_client = flow("embeddings-request"),
graph_embeddings_client = flow("graph-embeddings-request"),
triples_client = flow("triples-request"),
prompt_client = flow("prompt-request"),
verbose=True,
)
# Cache starts empty every time - no benefit from previous requests
response = await self.rag.query(...)
# VERY SHORT-LIVED: Query object created/destroyed per request
class GraphRag:
async def query(self, query, user="trustgraph", collection="default", ...):
q = Query(rag=self, user=user, collection=collection, ...) # Created
kg = await q.get_labelgraph(query) # Used briefly
# q automatically destroyed when function exits
```
**Arquitetura Otimizada e de Longa Duração:**
```python
class Processor(FlowProcessor):
def __init__(self, **params):
super().__init__(**params)
self.rag_instance = None # Will be initialized once
self.client_connections = {}
async def initialize_rag(self, flow):
"""Initialize GraphRag once, reuse for all requests"""
if self.rag_instance is None:
self.rag_instance = LongLivedGraphRag(
embeddings_client=flow("embeddings-request"),
graph_embeddings_client=flow("graph-embeddings-request"),
triples_client=flow("triples-request"),
prompt_client=flow("prompt-request"),
verbose=True,
)
return self.rag_instance
async def on_request(self, msg, consumer, flow):
# REUSE the same GraphRag instance - caches persist!
rag = await self.initialize_rag(flow)
# Query object becomes lightweight execution context
response = await rag.query_with_context(
query=v.query,
execution_context=QueryContext(
user=v.user,
collection=v.collection,
entity_limit=entity_limit,
# ... other params
)
)
class LongLivedGraphRag:
def __init__(self, ...):
# CONSERVATIVE caches - balance performance vs consistency
self.label_cache = LRUCacheWithTTL(max_size=5000, ttl=300) # 5min TTL for freshness
# Note: No embedding cache - already cached per-query, no cross-query benefit
# Note: No query result cache due to consistency concerns
self.performance_metrics = PerformanceTracker()
async def query_with_context(self, query: str, context: QueryContext):
# Use lightweight QueryExecutor instead of heavyweight Query object
executor = QueryExecutor(self, context) # Minimal object
return await executor.execute(query)
@dataclass
class QueryContext:
"""Lightweight execution context - no heavy operations"""
user: str
collection: str
entity_limit: int
triple_limit: int
max_subgraph_size: int
max_path_length: int
class QueryExecutor:
"""Lightweight execution context - replaces old Query class"""
def __init__(self, rag: LongLivedGraphRag, context: QueryContext):
self.rag = rag
self.context = context
# No heavy initialization - just references
async def execute(self, query: str):
# All heavy lifting uses persistent rag caches
return await self.rag.execute_optimized_query(query, self.context)
```
Esta mudança arquitetural oferece:
**Redução de 10-20% nas consultas ao banco de dados** para grafos com relacionamentos comuns (em comparação com 0% atualmente)
**Eliminação da sobrecarga de criação de objetos** para cada requisição
**Pool de conexões persistentes** e reutilização do cliente
**Otimização entre requisições** dentro das janelas de tempo de vida (TTL) do cache
<<<<<<< HEAD
**Limitação Importante de Consistência do Cache:**
O cache de longo prazo introduz o risco de dados desatualizados quando entidades/rótulos são excluídos ou modificados no grafo subjacente. O cache LRU com TTL oferece um equilíbrio entre ganhos de desempenho e frescor dos dados, mas não pode detectar alterações em tempo real no grafo.
=======
**Importante Limitação de Consistência do Cache:**
O cache de longo prazo introduz o risco de dados desatualizados quando entidades/rótulos são excluídos ou modificados no grafo subjacente. O cache LRU com TTL oferece um equilíbrio entre ganhos de desempenho e frescor dos dados, mas não detecta alterações em tempo real no grafo.
>>>>>>> 82edf2d (New md files from RunPod)
#### Fase 1: Otimização de Traversal de Grafos
**Problemas na Implementação Atual:**
```python
# INEFFICIENT: 3 queries per entity per level
async def follow_edges(self, ent, subgraph, path_length):
# Query 1: s=ent, p=None, o=None
res = await self.rag.triples_client.query(s=ent, p=None, o=None, limit=self.triple_limit)
# Query 2: s=None, p=ent, o=None
res = await self.rag.triples_client.query(s=None, p=ent, o=None, limit=self.triple_limit)
# Query 3: s=None, p=None, o=ent
res = await self.rag.triples_client.query(s=None, p=None, o=ent, limit=self.triple_limit)
```
**Implementação Otimizada:**
```python
async def optimized_traversal(self, entities: List[str], max_depth: int) -> Set[Triple]:
visited = set()
current_level = set(entities)
subgraph = set()
for depth in range(max_depth):
if not current_level or len(subgraph) >= self.max_subgraph_size:
break
# Batch all queries for current level
batch_queries = []
for entity in current_level:
if entity not in visited:
batch_queries.extend([
TripleQuery(s=entity, p=None, o=None),
TripleQuery(s=None, p=entity, o=None),
TripleQuery(s=None, p=None, o=entity)
])
# Execute all queries concurrently
results = await self.execute_batch_queries(batch_queries)
# Process results and prepare next level
next_level = set()
for result in results:
subgraph.update(result.triples)
next_level.update(result.new_entities)
visited.update(current_level)
current_level = next_level - visited
return subgraph
```
#### Fase 2: Resolução Paralela de Rótulos
**Implementação Sequencial Atual:**
```python
# INEFFICIENT: Sequential processing
for edge in subgraph:
s = await self.maybe_label(edge[0]) # Individual query
p = await self.maybe_label(edge[1]) # Individual query
o = await self.maybe_label(edge[2]) # Individual query
```
**Implementação Paralela Otimizada:**
```python
async def resolve_labels_parallel(self, subgraph: List[Triple]) -> List[Triple]:
# Collect all unique entities needing labels
entities_to_resolve = set()
for s, p, o in subgraph:
entities_to_resolve.update([s, p, o])
# Remove already cached entities
uncached_entities = [e for e in entities_to_resolve if e not in self.label_cache]
# Batch query for all uncached labels
if uncached_entities:
label_results = await self.batch_label_query(uncached_entities)
self.label_cache.update(label_results)
# Apply labels to subgraph
return [
(self.label_cache.get(s, s), self.label_cache.get(p, p), self.label_cache.get(o, o))
for s, p, o in subgraph
]
```
#### Fase 3: Estratégia Avançada de Cache
**Cache LRU com TTL:**
```python
class LRUCacheWithTTL:
def __init__(self, max_size: int, default_ttl: int = 3600):
self.cache = OrderedDict()
self.max_size = max_size
self.default_ttl = default_ttl
self.access_times = {}
async def get(self, key: str) -> Optional[Any]:
if key in self.cache:
# Check TTL expiration
if time.time() - self.access_times[key] > self.default_ttl:
del self.cache[key]
del self.access_times[key]
return None
# Move to end (most recently used)
self.cache.move_to_end(key)
return self.cache[key]
return None
async def put(self, key: str, value: Any):
if key in self.cache:
self.cache.move_to_end(key)
else:
if len(self.cache) >= self.max_size:
# Remove least recently used
oldest_key = next(iter(self.cache))
del self.cache[oldest_key]
del self.access_times[oldest_key]
self.cache[key] = value
self.access_times[key] = time.time()
```
#### Fase 4: Otimização de Consulta e Monitoramento
**Coleta de Métricas de Desempenho:**
```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
```
**Tempo Limite de Consulta e Disjuntor:**
```python
async def execute_with_timeout(self, query_func, timeout: int = 30):
try:
return await asyncio.wait_for(query_func(), timeout=timeout)
except asyncio.TimeoutError:
logger.error(f"Query timeout after {timeout}s")
raise GraphRagTimeoutError(f"Query exceeded timeout of {timeout}s")
```
## Considerações sobre a Consistência do Cache
**Compensações entre a Atualidade dos Dados:**
<<<<<<< HEAD
**Cache de rótulos (TTL de 5 minutos)**: Risco de exibir rótulos de entidades excluídas/renomeadas.
**Sem cache de embeddings**: Não é necessário - os embeddings já são armazenados em cache por consulta.
**Sem cache de resultados**: Impede que resultados de subgrafos desatualizados sejam exibidos devido à exclusão de entidades/relacionamentos.
**Estratégias de Mitigação:**
**Valores de TTL conservadores**: Equilibre os ganhos de desempenho (10-20%) com a atualização dos dados.
**Hooks de invalidação de cache**: Integração opcional com eventos de mutação do grafo.
**Painéis de monitoramento**: Acompanhe as taxas de acerto do cache versus incidentes de desatualização.
**Políticas de cache configuráveis**: Permite ajustes específicos para cada implantação, com base na frequência de mutação.
**Configuração de Cache Recomendada pela Taxa de Mutação do Grafo:**
**Alta mutação (>100 alterações/hora)**: TTL=60s, tamanhos de cache menores.
**Média mutação (10-100 alterações/hora)**: TTL=300s (padrão).
**Baixa mutação (<10 alterações/hora)**: TTL=600s, tamanhos de cache maiores.
=======
**Cache de rótulos (TTL de 5 minutos)**: Risco de servir rótulos de entidades excluídas/renomeadas
**Sem cache de embeddings**: Não necessário - os embeddings já são armazenados em cache por consulta
**Sem cache de resultados**: Impede que resultados de subgrafos desatualizados sejam retornados devido à exclusão de entidades/relacionamentos
**Estratégias de Mitigação:**
**Valores de TTL conservadores**: Equilibre os ganhos de desempenho (10-20%) com a atualização dos dados
**Hooks de invalidação de cache**: Integração opcional com eventos de mutação do grafo
**Painéis de monitoramento**: Acompanhe as taxas de acerto do cache versus incidentes de desatualização
**Políticas de cache configuráveis**: Permite ajustes específicos para cada implantação, com base na frequência de mutação
**Configuração de Cache Recomendada pela Taxa de Mutação do Grafo:**
**Alta mutação (>100 alterações/hora)**: TTL=60s, tamanhos de cache menores
**Média mutação (10-100 alterações/hora)**: TTL=300s (padrão)
**Baixa mutação (<10 alterações/hora)**: TTL=600s, tamanhos de cache maiores
>>>>>>> 82edf2d (New md files from RunPod)
## Considerações de Segurança
**Prevenção de Injeção de Consulta:**
<<<<<<< HEAD
Valide todos os identificadores de entidade e parâmetros de consulta.
Use consultas parametrizadas para todas as interações com o banco de dados.
Implemente limites de complexidade de consulta para evitar ataques de negação de serviço (DoS).
**Proteção de Recursos:**
Aplique limites máximos de tamanho de subgrafo.
Implemente tempos limite de consulta para evitar o esgotamento de recursos.
Adicione monitoramento e limites de uso de memória.
**Controle de Acesso:**
Mantenha o isolamento existente de usuários e coleções.
Adicione registro de auditoria para operações que afetam o desempenho.
Implemente limitação de taxa para operações dispendiosas.
=======
Valide todos os identificadores de entidade e parâmetros de consulta
Use consultas parametrizadas para todas as interações com o banco de dados
Implemente limites de complexidade de consulta para evitar ataques de negação de serviço (DoS)
**Proteção de Recursos:**
Aplique limites máximos de tamanho de subgrafo
Implemente tempos limite de consulta para evitar o esgotamento de recursos
Adicione monitoramento e limites de uso de memória
**Controle de Acesso:**
Mantenha o isolamento existente de usuários e coleções
Adicione registro de auditoria para operações que afetam o desempenho
Implemente limitação de taxa para operações dispendiosas
>>>>>>> 82edf2d (New md files from RunPod)
## Considerações de Desempenho
### Melhorias de Desempenho Esperadas
**Redução de Consultas:**
<<<<<<< HEAD
Atual: ~9.000+ consultas para um pedido típico.
Otimizado: ~50-100 consultas agrupadas (redução de 98%).
**Melhorias no Tempo de Resposta:**
Travessia do grafo: 15-20s → 3-5s (4-5 vezes mais rápido).
Resolução de rótulos: 8-12s → 2-4s (3 vezes mais rápido).
Consulta geral: 25-35s → 6-10s (melhora de 3-4 vezes).
**Eficiência de Memória:**
Tamanhos de cache limitados evitam vazamentos de memória.
Estruturas de dados eficientes reduzem a pegada de memória em ~40%.
Melhor coleta de lixo através da limpeza adequada de recursos.
**Expectativas Realistas de Desempenho:**
**Cache de rótulos**: Redução de 10-20% nas consultas para grafos com relacionamentos comuns.
**Otimização de agrupamento**: Redução de 50-80% nas consultas (otimização primária).
**Otimização do ciclo de vida do objeto**: Elimina a sobrecarga de criação por pedido.
**Melhora geral**: Melhoria de 3-4 vezes no tempo de resposta, principalmente devido ao agrupamento.
**Melhorias de Escalabilidade:**
Suporte para grafos de conhecimento 3-5 vezes maiores (limitado pelas necessidades de consistência do cache).
Capacidade de solicitação concorrente 3-5 vezes maior.
Melhor utilização de recursos através da reutilização de conexões.
=======
Atual: ~9.000+ consultas para um pedido típico
Otimizado: ~50-100 consultas em lote (redução de 98%)
**Melhorias no Tempo de Resposta:**
Traversal do grafo: 15-20s → 3-5s (4-5x mais rápido)
Resolução de rótulos: 8-12s → 2-4s (3x mais rápido)
Consulta geral: 25-35s → 6-10s (melhora de 3-4x)
**Eficiência de Memória:**
Tamanhos de cache limitados evitam vazamentos de memória
Estruturas de dados eficientes reduzem a pegada de memória em ~40%
Melhor coleta de lixo através da limpeza adequada de recursos
**Expectativas Realistas de Desempenho:**
**Cache de rótulos**: Redução de 10-20% nas consultas para grafos com relacionamentos comuns
**Otimização de lote**: Redução de 50-80% nas consultas (otimização primária)
**Otimização do ciclo de vida do objeto**: Elimina a sobrecarga de criação por pedido
**Melhora geral**: Melhoria de 3-4x no tempo de resposta, principalmente devido ao lote
**Melhorias na Escalabilidade:**
Suporte para grafos de conhecimento 3-5x maiores (limitado pelas necessidades de consistência do cache)
Capacidade de solicitação concorrente 3-5x maior
Melhor utilização de recursos através da reutilização de conexões
>>>>>>> 82edf2d (New md files from RunPod)
### Monitoramento de Desempenho
**Métricas em Tempo Real:**
<<<<<<< HEAD
Tempos de execução de consultas por tipo de operação.
Taxas de acerto e eficácia do cache.
Utilização do pool de conexões do banco de dados.
Uso de memória e impacto da coleta de lixo.
=======
Tempos de execução de consultas por tipo de operação
Taxas de acerto e eficácia do cache
Utilização do pool de conexões do banco de dados
Uso de memória e impacto na coleta de lixo
>>>>>>> 82edf2d (New md files from RunPod)
**Benchmarking de Desempenho:**
Testes de regressão de desempenho automatizados
Testes de carga com volumes de dados realistas
Benchmarks de comparação com a implementação atual
## Estratégia de Testes
### Testes Unitários
<<<<<<< HEAD
Teste de componentes individuais para travessia, cache e resolução de rótulos
=======
Testes de componentes individuais para travessia, cache e resolução de rótulos
>>>>>>> 82edf2d (New md files from RunPod)
Simulações de interações com o banco de dados para testes de desempenho
Testes de expiração de cache e TTL
Tratamento de erros e cenários de timeout
### Testes de Integração
Testes de ponta a ponta de consultas GraphRAG com otimizações
Testes de interação com o banco de dados com dados reais
Tratamento de solicitações concorrentes e gerenciamento de recursos
Detecção de vazamentos de memória e verificação da limpeza de recursos
### Testes de Desempenho
Testes de benchmark contra a implementação atual
<<<<<<< HEAD
Testes de carga com tamanhos e complexidades de grafos variáveis
=======
Testes de carga com diferentes tamanhos e complexidades de grafos
>>>>>>> 82edf2d (New md files from RunPod)
Testes de estresse para limites de memória e conexões
Testes de regressão para melhorias de desempenho
### Testes de Compatibilidade
Verificar a compatibilidade da API GraphRAG existente
Testar com vários backends de banco de dados de grafos
Validar a precisão dos resultados em comparação com a implementação atual
## Plano de Implementação
### Abordagem de Implementação Direta
Como as APIs podem ser alteradas, implemente as otimizações diretamente sem a complexidade da migração:
1. **Substituir `follow_edges`**: Reescrever com travessia em lote iterativa
2. **Otimizar `get_labelgraph`**: Implementar resolução de rótulos paralela
3. **Adicionar GraphRag de longa duração**: Modificar o Processador para manter uma instância persistente
4. **Implementar cache de rótulos**: Adicionar um cache LRU com TTL à classe GraphRag
### Escopo das Alterações
**Classe de consulta**: Substituir ~50 linhas em `follow_edges`, adicionar ~30 linhas para tratamento em lote
**Classe GraphRag**: Adicionar uma camada de cache (~40 linhas)
**Classe Processador**: Modificar para usar uma instância persistente de GraphRag (~20 linhas)
**Total**: ~140 linhas de alterações focadas, principalmente dentro das classes existentes
## Cronograma
**Semana 1: Implementação Central**
Substituir `follow_edges` por travessia iterativa em lote
Implementar resolução de rótulos paralela em `get_labelgraph`
Adicionar uma instância de longa duração de GraphRag ao Processador
Implementar a camada de cache de rótulos
**Semana 2: Testes e Integração**
Testes unitários para a nova lógica de travessia e cache
Benchmarking de desempenho contra a implementação atual
Testes de integração com dados de grafos reais
Revisão de código e otimização
**Semana 3: Implantação**
Implantar a implementação otimizada
Monitorar as melhorias de desempenho
Ajustar o TTL do cache e os tamanhos do lote com base no uso real
## Perguntas Abertas
**Pool de Conexões do Banco de Dados**: Devemos implementar um pool de conexões personalizado ou usar o pool de conexões do cliente do banco de dados existente?
**Persistência do Cache**: Os caches de rótulos e embeddings devem persistir entre as reinicializações do serviço?
**Cache Distribuído**: Para implantações multi-instância, devemos implementar um cache distribuído com Redis/Memcached?
**Formato do Resultado da Consulta**: Devemos otimizar a representação interna da tripla para uma melhor eficiência de memória?
**Integração de Monitoramento**: Quais métricas devem ser expostas aos sistemas de monitoramento existentes (Prometheus, etc.)?
## Referências
[Implementação Original do GraphRAG](trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py)
[Princípios de Arquitetura do TrustGraph](architecture-principles.md)
[Especificação de Gerenciamento de Coleções](collection-management.md)

View file

@ -0,0 +1,710 @@
---
layout: default
title: "Especificação Técnica de Desligamento Gratuito de Importação/Exportação"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Desligamento Gratuito de Importação/Exportação
> **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.
## Declaração do Problema
Atualmente, o gateway TrustGraph experimenta perda de mensagens durante o fechamento do WebSocket tanto em operações de importação quanto de exportação. Isso ocorre devido a condições de corrida em que as mensagens em trânsito são descartadas antes de atingir seu destino (filas Pulsar para importações, clientes WebSocket para exportações).
### Problemas no Lado da Importação
1. O buffer da fila asyncio do publicador não é esvaziado durante o desligamento.
2. O WebSocket é fechado antes de garantir que as mensagens enfileiradas cheguem ao Pulsar.
3. Não há mecanismo de confirmação para a entrega bem-sucedida da mensagem.
### Problemas no Lado da Exportação
1. As mensagens são confirmadas no Pulsar antes da entrega bem-sucedida aos clientes.
2. Os tempos limite fixos causam a perda de mensagens quando as filas estão cheias.
3. Não há mecanismo de controle de fluxo para lidar com consumidores lentos.
4. Múltiplos pontos de buffer onde os dados podem ser perdidos.
## Visão Geral da Arquitetura
```
Import Flow:
Client -> Websocket -> TriplesImport -> Publisher -> Pulsar Queue
Export Flow:
Pulsar Queue -> Subscriber -> TriplesExport -> Websocket -> Client
```
## Correções Propostas
### 1. Melhorias no Publicador (Lado da Importação)
#### A. Esvaziamento Gratuito da Fila
**Arquivo**: `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))
```
**Principais Vantagens do Design:**
<<<<<<< HEAD
**Local de Envio Único**: Todas as chamadas de `producer.send()` ocorrem em um único local dentro do método `run()`.
=======
**Local de Envio Único**: Todas as chamadas `producer.send()` ocorrem em um único local dentro do método `run()`.
>>>>>>> 82edf2d (New md files from RunPod)
**Máquina de Estados Clara**: Três estados claros - em execução, esvaziando, parado.
**Proteção por Timeout**: Não fica indefinidamente travado durante o esvaziamento.
**Melhor Observabilidade**: Registro claro do progresso do esvaziamento e das transições de estado.
**Rejeição de Mensagens Opcional**: Pode rejeitar novas mensagens durante a fase de desligamento.
#### B. Ordem de Desligamento Aprimorada
**Arquivo**: `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. Melhorias para o Assinante (Lado de Exportação)
#### A. Padrão de Drenagem Integrado
**Arquivo**: `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
```
<<<<<<< HEAD
**Principais Vantagens do Design (compatível com o padrão do Editor):**
**Local de Processamento Único**: Todo o processamento de mensagens ocorre no método `run()`
**Máquina de Estados Clara**: Três estados claros - em execução, esvaziando, parado
**Pausa Durante o Esvaziamento**: Interrompe a aceitação de novas mensagens do Pulsar enquanto esvazia as filas existentes
**Proteção por Timeout**: Não fica indefinidamente travado durante o esvaziamento
=======
**Principais Vantagens do Design (compatível com o padrão do Publicador):**
**Local de Processamento Único**: Todo o processamento de mensagens ocorre no método `run()`
**Máquina de Estados Clara**: Três estados claros - em execução, esvaziando, parado
**Pausa Durante o Esvaziamento**: Interrompe a aceitação de novas mensagens do Pulsar enquanto esvazia as filas existentes
**Proteção por Timeout**: Não fica indefinidamente bloqueado durante o esvaziamento
>>>>>>> 82edf2d (New md files from RunPod)
**Limpeza Adequada**: Reconhece negativamente quaisquer mensagens não entregues durante o desligamento
#### B. Melhorias no Manipulador de Exportação
**Arquivo**: `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. Melhorias no Nível de Socket
**Arquivo**: `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
```
## Opções de Configuração
Adicionar suporte para configuração para ajustar o comportamento:
```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
```
## Estratégia de Testes
### Testes Unitários
```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
```
### Testes de Integração
```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
```
## Plano de Implementação
### Fase 1: Correções Críticas (Semana 1)
Corrigir o tempo de reconhecimento do assinante (evitar perda de mensagens)
Adicionar esvaziamento da fila do publicador
Implantar no ambiente de teste
### Fase 2: Desligamento Gradual (Semana 2)
<<<<<<< HEAD
Implementar coordenação de desligamento
=======
Implementar a coordenação de desligamento
>>>>>>> 82edf2d (New md files from RunPod)
Adicionar estratégias de backpressure
Testes de desempenho
### Fase 3: Monitoramento e Ajuste (Semana 3)
Adicionar métricas para profundidade da fila
Adicionar alertas para perda de mensagens
Ajustar valores de timeout com base em dados de produção
## Monitoramento e Alertas
### Métricas a serem Monitoradas
`publisher.queue.depth` - Tamanho atual da fila do publicador
`publisher.messages.dropped` - Mensagens perdidas durante o desligamento
`subscriber.messages.negatively_acknowledged` - Entregas falhadas
`websocket.graceful_shutdowns` - Desligamentos graduais bem-sucedidos
`websocket.forced_shutdowns` - Desligamentos forçados/por timeout
### Alertas
Profundidade da fila do publicador > 80% da capacidade
Qualquer perda de mensagens durante o desligamento
Taxa de reconhecimento negativo do assinante > 1%
Timeout de desligamento excedido
## Compatibilidade com Versões Anteriores
Todas as alterações mantêm a compatibilidade com versões anteriores:
Comportamento padrão inalterado sem configuração
Implantações existentes continuam a funcionar
Degradação gradual se novos recursos não estiverem disponíveis
## Considerações de Segurança
Nenhum novo vetor de ataque introduzido
O backpressure impede ataques de esgotamento de memória
<<<<<<< HEAD
Limites configuráveis evitam o abuso de recursos
=======
Limites configuráveis evitam o abuso de recursos
>>>>>>> 82edf2d (New md files from RunPod)
## Impacto no Desempenho
Sobrecarga mínima durante a operação normal
O desligamento pode levar até 5 segundos a mais (configurável)
O uso de memória é limitado pelos limites do tamanho da fila
O impacto na CPU é insignificante (<1% de aumento)

View file

@ -0,0 +1,504 @@
---
layout: default
title: "Especificação Técnica de Saída de Prompt JSONL"
parent: "Portuguese (Beta)"
---
<<<<<<< HEAD
# Especificação Técnica de Saída de Prompt JSONL
> **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.
=======
# Especificação Técnica da Saída de Prompt JSONL
>>>>>>> 82edf2d (New md files from RunPod)
## Visão Geral
Esta especificação descreve a implementação do formato de saída JSONL (JSON Lines)
<<<<<<< HEAD
para respostas de prompt no TrustGraph. JSONL permite a extração de dados estruturados
de forma resiliente à truncagem das respostas de LLM, abordando problemas críticos
relacionados à corrupção de saídas de matriz JSON quando as respostas de LLM atingem
=======
para respostas de prompt no TrustGraph. O JSONL permite a extração de dados estruturados
de forma resistente a truncamentos das respostas de LLM, abordando problemas críticos
relacionados à corrupção de saídas de matrizes JSON quando as respostas de LLM atingem
>>>>>>> 82edf2d (New md files from RunPod)
os limites de tokens de saída.
Esta implementação suporta os seguintes casos de uso:
1. **Extração Resistente à Truncagem**: Extrair resultados parciais válidos mesmo quando
a saída do LLM é truncada no meio da resposta.
2. **Extração em Larga Escala**: Lidar com a extração de muitos itens sem risco de
falha completa devido a limites de tokens.
3. **Extração de Tipos Mistos**: Suportar a extração de vários tipos de entidades
(definições, relacionamentos, entidades, atributos) em um único prompt.
4. **Saída Compatível com Streaming**: Permitir o processamento futuro de streaming/incremental
dos resultados da extração.
## Objetivos
**Compatibilidade com versões anteriores**: As instruções existentes que usam `response-type: "text"` e
`response-type: "json"` continuam a funcionar sem modificação.
**Resiliência à truncagem**: Saídas parciais do LLM produzem resultados válidos parciais
em vez de falha completa.
**Validação de esquema**: Suporte à validação de esquema JSON para objetos individuais.
**Uniões discriminadas**: Suporte a saídas de tipos mistos usando um campo `type`
discriminador.
**Alterações mínimas na API**: Estenda a configuração de instruções existente com um novo
tipo de resposta e chave de esquema.
## Contexto
### Arquitetura atual
O serviço de instruções suporta dois tipos de resposta:
1. `response-type: "text"` - Resposta de texto bruto retornada como está.
2. `response-type: "json"` - JSON analisado da resposta, validado contra
um `schema` opcional.
Implementação atual em `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
```
### Limitações Atuais
Quando os prompts de extração solicitam a saída como arrays JSON (`[{...}, {...}, ...]`):
**Corrupção por truncamento**: Se o LLM atinge os limites de tokens de saída no meio do array, a
resposta inteira se torna JSON inválido e não pode ser analisada.
**Análise "tudo ou nada"**: É necessário receber a saída completa antes de analisar.
**Sem resultados parciais**: Uma resposta truncada produz zero dados utilizáveis.
**Não confiável para grandes extrações**: Quanto mais itens extraídos, maior o risco de falha.
<<<<<<< HEAD
Esta especificação aborda essas limitações introduzindo o formato JSONL para
=======
Esta especificação aborda essas limitações, introduzindo o formato JSONL para
>>>>>>> 82edf2d (New md files from RunPod)
prompts de extração, onde cada item extraído é um objeto JSON completo em sua
própria linha.
## Design Técnico
### Extensão do Tipo de Resposta
Adicione um novo tipo de resposta `"jsonl"`, juntamente com os tipos existentes `"text"` e `"json"`.
#### Alterações de Configuração
**Novo valor do tipo de resposta:**
```
"response-type": "jsonl"
```
**Interpretação do esquema:**
A chave existente `"schema"` é usada tanto para o tipo de resposta `"json"` quanto para o tipo de resposta `"jsonl"`.
A interpretação depende do tipo de resposta:
`"json"`: O esquema descreve toda a resposta (geralmente um array ou objeto).
`"jsonl"`: O esquema descreve cada linha/objeto individual.
```json
{
"response-type": "jsonl",
"schema": {
"type": "object",
"properties": {
"entity": { "type": "string" },
"definition": { "type": "string" }
},
"required": ["entity", "definition"]
}
}
```
Isso evita alterações nas ferramentas de configuração e nos editores.
### Especificação do Formato JSONL
#### Extração Simples
Para prompts que extraem um único tipo de objeto (definições, relacionamentos,
tópicos, linhas), a saída é um objeto JSON por linha, sem wrapper:
**Formato de saída do prompt:**
```
{"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"}
{"entity": "chlorophyll", "definition": "Green pigment in plants"}
{"entity": "mitochondria", "definition": "Powerhouse of the cell"}
```
<<<<<<< HEAD
**Contraste com o formato anterior de array JSON:**
=======
**Contraste com o formato de array JSON anterior:**
>>>>>>> 82edf2d (New md files from RunPod)
```json
[
{"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"},
{"entity": "chlorophyll", "definition": "Green pigment in plants"},
{"entity": "mitochondria", "definition": "Powerhouse of the cell"}
]
```
Se o LLM truncar após a linha 2, o formato de array JSON resulta em JSON inválido,
<<<<<<< HEAD
enquanto o JSONL produz dois objetos válidos.
=======
enquanto o JSONL gera dois objetos válidos.
>>>>>>> 82edf2d (New md files from RunPod)
#### Extração de Tipos Mistos (Uniões Discriminadas)
Para prompts que extraem vários tipos de objetos (por exemplo, definições e
relacionamentos, ou entidades, relacionamentos e atributos), use um campo `"type"`
como discriminador:
**Formato de saída do prompt:**
```
{"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 uniões discriminadas usa `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"]
}
]
}
}
```
#### Extração de Ontologia
Para extração baseada em ontologia com entidades, relacionamentos e atributos:
**Formato de saída do 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"}
```
### Detalhes de Implementação
#### Classe Prompt
A classe `Prompt` existente não requer alterações. O campo `schema` é reutilizado
para JSONL, com sua interpretação 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
<<<<<<< HEAD
Nenhuma alteração necessária - o carregamento da configuração existente já lida com a
`schema` chave.
=======
Nenhuma alteração necessária - o carregamento da configuração existente já trata da
chave `schema`.
>>>>>>> 82edf2d (New md files from RunPod)
#### Análise de JSONL
Adicionar um novo método de análise para respostas 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
```
#### Alterações no PromptManager.invoke
Estenda o método invoke para lidar com o novo tipo de resposta:
```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 Afetados
Os seguintes prompts devem ser migrados para o formato JSONL:
| ID do Prompt | Descrição | Campo de Tipo |
|-----------|-------------|------------|
| `extract-definitions` | Extração de entidade/definição | Não (tipo único) |
| `extract-relationships` | Extração de relacionamento | Não (tipo único) |
| `extract-topics` | Extração de tópico/definição | Não (tipo único) |
| `extract-rows` | Extração de linha estruturada | Não (tipo único) |
| `agent-kg-extract` | Extração combinada de definição + relacionamento | Sim: `"definition"`, `"relationship"` |
| `extract-with-ontologies` / `ontology-extract` | Extração baseada em ontologia | Sim: `"entity"`, `"relationship"`, `"attribute"` |
### Alterações na API
#### Perspectiva do Cliente
A análise JSONL é transparente para os chamadores da API do serviço de prompt. A análise ocorre
no lado do servidor no serviço de prompt, e a resposta é retornada através do campo padrão
`PromptResponse.object` como um array JSON serializado.
Quando os clientes chamam o serviço de prompt (via `PromptClient.prompt()` ou similar):
**`response-type: "json"`** com esquema de array → o cliente recebe `list` do Python
**`response-type: "jsonl"`** → o cliente recebe `list` do Python
Da perspectiva do cliente, ambos retornam estruturas de dados idênticas. A
diferença está inteiramente em como a saída do LLM é analisada no lado do servidor:
Formato de array JSON: Uma única chamada `json.loads()`; falha completamente se truncado
Formato JSONL: Análise linha por linha; produz resultados parciais se truncado
Isso significa que o código do cliente existente que espera uma lista de prompts de extração
não requer alterações ao migrar prompts de JSON para JSONL.
#### Valor de Retorno do Servidor
Para `response-type: "jsonl"`, o método `PromptManager.invoke()` retorna um
`list[dict]` contendo todos os objetos analisados e validados com sucesso. Esta
lista é então serializada para JSON para o campo `PromptResponse.object`.
#### Tratamento de Erros
Resultados vazios: Retorna uma lista vazia `[]` com um log de aviso
Falha parcial na análise: Retorna uma lista de objetos analisados com sucesso com
logs de aviso para as falhas
Falha completa na análise: Retorna uma lista vazia `[]` com logs de aviso
Isso difere de `response-type: "json"`, que lança `RuntimeError` em
caso de falha na análise. O comportamento tolerante para JSONL é intencional para fornecer
resiliência à truncagem.
### Exemplo de Configuração
Exemplo completo de configuração 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"]
}
}
```
## Considerações de Segurança
**Validação de Entrada**: A análise JSON utiliza o padrão `json.loads()`, que é seguro
contra ataques de injeção.
**Validação de Esquema**: Utiliza `jsonschema.validate()` para a aplicação do esquema.
<<<<<<< HEAD
**Sem Nova Superfície de Ataque**: A análise de JSONL é estritamente mais segura do que a análise de arrays JSON
=======
**Sem Nova Superfície de Ataque**: A análise JSONL é estritamente mais segura do que a análise de arrays JSON
>>>>>>> 82edf2d (New md files from RunPod)
devido ao processamento linha por linha.
## Considerações de Desempenho
**Memória**: A análise linha por linha usa menos memória máxima do que o carregamento de arrays JSON completos.
**Latência**: O desempenho da análise é comparável à análise de arrays JSON.
**Validação**: A validação de esquema é executada por objeto, o que adiciona sobrecarga, mas
permite resultados parciais em caso de falha na validação.
<<<<<<< HEAD
## Estratégia de Testes
=======
## Estratégia de Testes
>>>>>>> 82edf2d (New md files from RunPod)
### Testes Unitários
Análise de JSONL com entrada válida
Análise de JSONL com linhas vazias
Análise de JSONL com blocos de código Markdown
Análise de JSONL com linha final truncada
Análise de JSONL com linhas JSON inválidas intercaladas
Validação de esquema com uniões discriminadas `oneOf`
Compatibilidade com versões anteriores: prompts `"text"` e `"json"` existentes permanecem inalterados
### Testes de Integração
Extração ponta a ponta com prompts JSONL
Extração com truncamento simulado (resposta artificialmente limitada)
Extração de tipos mistos com discriminador de tipo
Extração de ontologia com todos os três tipos
### Testes de Qualidade de Extração
Compare os resultados da extração: formato JSONL versus array JSON.
Verifique a resiliência à truncagem: o JSONL produz resultados parciais onde o JSON falha.
## Plano de Migração
### Fase 1: Implementação
1. Implemente o método `parse_jsonl()` em `PromptManager`.
2. Estenda `invoke()` para lidar com `response-type: "jsonl"`.
3. Adicione testes unitários.
### Fase 2: Migração de Prompts
1. Atualize o prompt e a configuração `extract-definitions`.
2. Atualize o prompt e a configuração `extract-relationships`.
3. Atualize o prompt e a configuração `extract-topics`.
4. Atualize o prompt e a configuração `extract-rows`.
5. Atualize o prompt e a configuração `agent-kg-extract`.
6. Atualize o prompt e a configuração `extract-with-ontologies`.
### Fase 3: Atualizações para Sistemas Dependentes
1. Atualize qualquer código que consuma os resultados da extração para lidar com o tipo de retorno de lista.
2. Atualize o código que categoriza extrações de tipos mistos pelo campo `type`.
<<<<<<< HEAD
3. Atualize os testes que afirmam o formato da saída da extração.
=======
3. Atualize os testes que afirmam o formato de saída da extração.
>>>>>>> 82edf2d (New md files from RunPod)
## Perguntas Abertas
Nenhuma neste momento.
## Referências
Implementação atual: `trustgraph-flow/trustgraph/template/prompt_manager.py`
Especificação JSON Lines: https://jsonlines.org/
Esquema JSON `oneOf`: https://json-schema.org/understanding-json-schema/reference/combining.html#oneof
Especificação relacionada: Streaming LLM Responses (`docs/tech-specs/streaming-llm-responses.md`)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,358 @@
---
layout: default
title: "Estratégia de Registro (Logging) do TrustGraph"
parent: "Portuguese (Beta)"
---
# Estratégia de Registro (Logging) do 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.
## Visão Geral
O TrustGraph utiliza o módulo `logging` integrado do Python para todas as operações de registro, com configuração centralizada e integração opcional com o Loki para agregação de logs. Isso fornece uma abordagem padronizada e flexível para o registro em todos os componentes do sistema.
## Configuração Padrão
### Nível de Registro
**Nível Padrão**: `INFO`
**Configurável via**: argumento de linha de comando `--log-level`
**Opções**: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
### Destinos de Saída
1. **Console (stdout)**: Sempre habilitado - garante compatibilidade com ambientes conteinerizados.
2. **Loki**: Agregação de logs centralizada opcional (habilitada por padrão, pode ser desabilitada).
## Módulo de Registro Centralizado
Toda a configuração de registro é gerenciada pelo módulo `trustgraph.base.logging`, que fornece:
`add_logging_args(parser)` - Adiciona argumentos de linha de comando padrão para registro.
`setup_logging(args)` - Configura o registro a partir de argumentos analisados.
Este módulo é usado por todos os componentes do lado do servidor:
Serviços baseados em AsyncProcessor.
API Gateway.
Servidor MCP.
## Diretrizes de Implementação
### 1. Inicialização do Logger
Cada módulo deve criar seu próprio logger usando o módulo `__name__`:
```python
import logging
logger = logging.getLogger(__name__)
```
O nome do logger é automaticamente usado como um rótulo no Loki para filtragem e pesquisa.
### 2. Inicialização do Serviço
Todos os serviços do lado do servidor recebem automaticamente a configuração de registro através do módulo centralizado:
```python
from trustgraph.base import add_logging_args, setup_logging
import argparse
def main():
parser = argparse.ArgumentParser()
# Add standard logging arguments (includes Loki configuration)
add_logging_args(parser)
# Add your service-specific arguments
parser.add_argument('--port', type=int, default=8080)
args = parser.parse_args()
args = vars(args)
# Setup logging early in startup
setup_logging(args)
# Rest of your service initialization
logger = logging.getLogger(__name__)
logger.info("Service starting...")
```
### 3. Argumentos de Linha de Comando
Todos os serviços suportam estes argumentos de registro:
**Nível de Log:**
```bash
--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
```
**Configuração do Loki:**
```bash
--loki-enabled # Enable Loki (default)
--no-loki-enabled # Disable Loki
--loki-url URL # Loki push URL (default: http://loki:3100/loki/api/v1/push)
--loki-username USERNAME # Optional authentication
--loki-password PASSWORD # Optional authentication
```
**Exemplos:**
```bash
# Default - INFO level, Loki enabled
./my-service
# Debug mode, console only
./my-service --log-level DEBUG --no-loki-enabled
# Custom Loki server with auth
./my-service --loki-url http://loki.prod:3100/loki/api/v1/push \
--loki-username admin --loki-password secret
```
### 4. Variáveis de Ambiente
A configuração do Loki suporta a utilização de variáveis de ambiente como valores padrão:
```bash
export LOKI_URL=http://loki.prod:3100/loki/api/v1/push
export LOKI_USERNAME=admin
export LOKI_PASSWORD=secret
```
Os argumentos da linha de comando têm precedência sobre as variáveis de ambiente.
### 5. Melhores Práticas de Registro (Logging)
#### Uso dos Níveis de Log
**DEBUG**: Informações detalhadas para diagnosticar problemas (valores de variáveis, entrada/saída de funções)
**INFO**: Mensagens informativas gerais (serviço iniciado, configuração carregada, marcos de processamento)
**WARNING**: Mensagens de aviso para situações potencialmente perigosas (recursos obsoletos, erros recuperáveis)
**ERROR**: Mensagens de erro para problemas graves (operações com falha, exceções)
**CRITICAL**: Mensagens críticas para falhas do sistema que exigem atenção imediata
#### Formato da Mensagem
```python
# Good - includes context
logger.info(f"Processing document: {doc_id}, size: {doc_size} bytes")
logger.error(f"Failed to connect to database: {error}", exc_info=True)
# Avoid - lacks context
logger.info("Processing document")
logger.error("Connection failed")
```
#### Considerações de Desempenho
```python
# Use lazy formatting for expensive operations
logger.debug("Expensive operation result: %s", expensive_function())
# Check log level for very expensive debug operations
if logger.isEnabledFor(logging.DEBUG):
debug_data = compute_expensive_debug_info()
logger.debug(f"Debug data: {debug_data}")
```
### 6. Registro Estruturado com Loki
Para dados complexos, use o registro estruturado com tags adicionais para o Loki:
```python
logger.info("Request processed", extra={
'tags': {
'request_id': request_id,
'user_id': user_id,
'status': 'success'
}
})
```
Essas tags se tornam rótulos pesquisáveis no Loki, além de rótulos automáticos:
`severity` - Nível de log (DEBUG, INFO, WARNING, ERROR, CRITICAL)
`logger` - Nome do módulo (de `__name__`)
### 7. Registro de Exceções
Sempre inclua rastreamentos de pilha para exceções:
```python
try:
process_data()
except Exception as e:
logger.error(f"Failed to process data: {e}", exc_info=True)
raise
```
### 8. Considerações sobre o Logging Assíncrono
O sistema de logging utiliza manipuladores enfileirados não bloqueantes para o Loki:
A saída para o console é síncrona (rápida)
A saída para o Loki é enfileirada com um buffer de 500 mensagens
Uma thread em segundo plano gerencia a transmissão para o Loki
Não há bloqueio do código principal da aplicação
```python
import asyncio
import logging
async def async_operation():
logger = logging.getLogger(__name__)
# Logging is thread-safe and won't block async operations
logger.info(f"Starting async operation in task: {asyncio.current_task().get_name()}")
```
## Integração com Loki
### Arquitetura
O sistema de registro utiliza os recursos nativos de Python `QueueHandler` e `QueueListener` para a integração não bloqueante com o Loki:
1. **QueueHandler**: Os registros são colocados em uma fila de 500 mensagens (não bloqueante).
2. **Thread em Segundo Plano**: O QueueListener envia os registros para o Loki de forma assíncrona.
3. **Degradação Graciosa**: Se o Loki estiver indisponível, o registro no console continua.
### Rótulos Automáticos
Cada registro enviado para o Loki inclui:
`processor`: Identidade do processador (por exemplo, `config-svc`, `text-completion`, `embeddings`).
`severity`: Nível de registro (DEBUG, INFO, etc.).
`logger`: Nome do módulo (por exemplo, `trustgraph.gateway.service`, `trustgraph.agent.react.service`).
### Rótulos Personalizados
Adicione rótulos personalizados através do parâmetro `extra`:
```python
logger.info("User action", extra={
'tags': {
'user_id': user_id,
'action': 'document_upload',
'collection': collection_name
}
})
```
### Consultando Logs no Loki
```logql
# All logs from a specific processor (recommended - matches Prometheus metrics)
{processor="config-svc"}
{processor="text-completion"}
{processor="embeddings"}
# Error logs from a specific processor
{processor="config-svc", severity="ERROR"}
# Error logs from all processors
{severity="ERROR"}
# Logs from a specific processor with text filter
{processor="text-completion"} |= "Processing"
# All logs from API gateway
{processor="api-gateway"}
# Logs from processors matching pattern
{processor=~".*-completion"}
# Logs with custom tags
{processor="api-gateway"} | json | user_id="12345"
```
### Degradação Graciosa
Se o Loki estiver indisponível ou `python-logging-loki` não estiver instalado:
Mensagem de aviso impressa no console
O registro no console continua normalmente
O aplicativo continua em execução
Sem lógica de repetição para a conexão com o Loki (falha rápida, degrade graciosamente)
## Testes
Durante os testes, considere usar uma configuração de registro diferente:
```python
# In test setup
import logging
# Reduce noise during tests
logging.getLogger().setLevel(logging.WARNING)
# Or disable Loki for tests
setup_logging({'log_level': 'WARNING', 'loki_enabled': False})
```
## Integração de Monitoramento
### Formato Padrão
Todos os logs utilizam um formato consistente:
```
2025-01-09 10:30:45,123 - trustgraph.gateway.service - INFO - Request processed
```
Componentes de formatação:
Timestamp (formato ISO com milissegundos)
Nome do logger (caminho do módulo)
Nível de log
Mensagem
### Consultas Loki para Monitoramento
Consultas de monitoramento comuns:
```logql
# Error rate by processor
rate({severity="ERROR"}[5m]) by (processor)
# Top error-producing processors
topk(5, count_over_time({severity="ERROR"}[1h]) by (processor))
# Recent errors with processor name
{severity="ERROR"} | line_format "{{.processor}}: {{.message}}"
# All agent processors
{processor=~".*agent.*"} |= "exception"
# Specific processor error count
count_over_time({processor="config-svc", severity="ERROR"}[1h])
```
## Considerações de Segurança
**Nunca registre informações sensíveis** (senhas, chaves de API, dados pessoais, tokens)
**Sanitize a entrada do usuário** antes de registrar
**Use espaços reservados** para campos sensíveis: `user_id=****1234`
**Autenticação Loki**: Use `--loki-username` e `--loki-password` para implantações seguras
**Transporte seguro**: Use HTTPS para a URL do Loki em produção: `https://loki.prod:3100/loki/api/v1/push`
## Dependências
O módulo de registro centralizado requer:
`python-logging-loki` - Para integração com o Loki (opcional, degradação graciosa se ausente)
Já incluído em `trustgraph-base/pyproject.toml` e `requirements.txt`.
## Caminho de Migração
Para código existente:
1. **Serviços que já usam AsyncProcessor**: Nenhuma alteração necessária, o suporte ao Loki é automático
2. **Serviços que não usam AsyncProcessor** (api-gateway, mcp-server): Já foram atualizados
3. **Ferramentas de linha de comando (CLI)**: Fora do escopo - continue usando print() ou registro simples
### De print() para registro:
```python
# Before
print(f"Processing document {doc_id}")
# After
logger = logging.getLogger(__name__)
logger.info(f"Processing document {doc_id}")
```
## Resumo da Configuração
| Argumento | Padrão | Variável de Ambiente | Descrição |
|----------|---------|---------------------|-------------|
| `--log-level` | `INFO` | - | Nível de log do console e do Loki |
| `--loki-enabled` | `True` | - | Habilitar o registro de log do Loki |
| `--loki-url` | `http://loki:3100/loki/api/v1/push` | `LOKI_URL` | Endpoint de envio do Loki |
| `--loki-username` | `None` | `LOKI_USERNAME` | Nome de usuário de autenticação do Loki |
| `--loki-password` | `None` | `LOKI_PASSWORD` | Senha de autenticação do Loki |

View file

@ -0,0 +1,264 @@
---
layout: default
title: "Especificação de Argumentos da Ferramenta MCP"
parent: "Portuguese (Beta)"
---
# Especificação de Argumentos da Ferramenta 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.
## Visão Geral
**Nome da Funcionalidade**: Suporte a Argumentos da Ferramenta MCP
**Autor**: Claude Code Assistant
**Data**: 2025-08-21
**Status**: Finalizado
### Resumo Executivo
Permitir que agentes ReACT invoquem ferramentas MCP (Protocolo de Contexto do Modelo) com
argumentos definidos de forma adequada, adicionando suporte à especificação de argumentos às
configurações das ferramentas MCP, de forma semelhante a como as ferramentas de modelo de prompt
funcionam atualmente.
### Declaração do Problema
Atualmente, as ferramentas MCP no framework do agente ReACT não podem especificar seus
argumentos esperados. O método `McpToolImpl.get_arguments()` retorna
uma lista vazia, forçando os LLMs a adivinhar a estrutura correta de parâmetros
com base apenas nos nomes e descrições das ferramentas. Isso leva a:
Invocação de ferramentas não confiável devido à suposição de parâmetros
Má experiência do usuário quando as ferramentas falham devido a argumentos incorretos
Ausência de validação dos parâmetros da ferramenta antes da execução
Documentação de parâmetros ausente nos prompts do agente
### Objetivos
[ ] Permitir que as configurações da ferramenta MCP especifiquem os argumentos esperados (nome, tipo, descrição)
[ ] Atualizar o gerenciador de agentes para expor os argumentos da ferramenta MCP aos LLMs por meio de prompts
[ ] Manter a compatibilidade com versões anteriores com as configurações existentes da ferramenta MCP
[ ] Suportar a validação de argumentos semelhante às ferramentas de modelo de prompt
### Não Objetivos
Descoberta dinâmica de argumentos a partir de servidores MCP (melhoria futura)
Validação de tipo de argumento além da estrutura básica
Esquemas de argumentos complexos (objetos aninhados, arrays)
## Contexto e Informações de Base
### Estado Atual
As ferramentas MCP são configuradas no sistema de agente ReACT com metadados mínimos:
```json
{
"type": "mcp-tool",
"name": "get_bank_balance",
"description": "Get bank account balance",
"mcp-tool": "get_bank_balance"
}
```
O método `McpToolImpl.get_arguments()` retorna `[]`, portanto, os LLMs não recebem orientação de argumentos em seus prompts.
### Limitações
1. **Sem especificação de argumentos**: As ferramentas MCP não podem definir parâmetros esperados.
parâmetros.
2. **Adivinhação de parâmetros do LLM**: Os agentes devem inferir parâmetros a partir dos nomes/descrições das ferramentas.
3. **Informações do prompt ausentes**: Os prompts dos agentes não mostram detalhes dos argumentos para as ferramentas MCP.
4. **Sem validação**: Parâmetros inválidos são detectados apenas no momento da execução da ferramenta MCP.
### Componentes Relacionados
**trustgraph-flow/agent/react/service.py**: Carregamento de configuração de ferramentas e criação do AgentManager.
**trustgraph-flow/agent/react/tools.py**: Implementação do McpToolImpl.
**trustgraph-flow/agent/react/agent_manager.py**: Geração de prompts com argumentos de ferramentas.
**trustgraph-cli**: Ferramentas de linha de comando para gerenciamento de ferramentas MCP.
**Workbench**: Interface de usuário externa para configuração de ferramentas de agente.
## Requisitos
### Requisitos Funcionais
## Requisitos
### Requisitos Funcionais
1. **Argumentos de Configuração da Ferramenta MCP**: As configurações da ferramenta MCP DEVEM suportar um array opcional `arguments` com campos de nome, tipo e descrição.
2. **Exposição de Argumentos**: `McpToolImpl.get_arguments()` DEVE retornar os argumentos configurados em vez de uma lista vazia.
3. **Integração com Prompts**: Os prompts do agente DEVEM incluir detalhes dos argumentos da ferramenta MCP quando os argumentos forem especificados.
4. **Compatibilidade com Versões Anteriores**: As configurações existentes da ferramenta MCP sem argumentos DEVEM continuar a funcionar.
5. **Suporte para CLI**: A CLI existente `tg-invoke-mcp-tool` suporta argumentos (já implementado).
### Requisitos Não Funcionais
1. **Compatibilidade com Versões Anteriores**: Nenhuma alteração disruptiva para as configurações existentes da ferramenta MCP.
2. **Desempenho**: Nenhum impacto significativo no desempenho da geração de prompts do agente.
3. **Consistência**: O tratamento de argumentos DEVE corresponder aos padrões de ferramentas de modelos de prompt.
### Histórias de Usuário
1. Como um **desenvolvedor de agente**, quero especificar argumentos da ferramenta MCP na configuração para que os LLMs possam invocar ferramentas com parâmetros corretos.
2. Como um **usuário da workbench**, quero configurar argumentos da ferramenta MCP na interface do usuário para que os agentes usem as ferramentas corretamente.
3. Como um **LLM em um agente ReACT**, quero ver as especificações dos argumentos da ferramenta nos prompts para que eu possa fornecer parâmetros corretos.
## Design
### Arquitetura de Alto Nível
Estenda a configuração da ferramenta MCP para corresponder ao padrão do modelo de prompt, adicionando:
1. Um array opcional `arguments` às configurações da ferramenta MCP.
2. Modificações em `McpToolImpl` para aceitar e retornar argumentos configurados.
3. Atualizações no carregamento da configuração para lidar com os argumentos da ferramenta MCP.
4. Garantir que os prompts do agente incluam informações sobre os argumentos da ferramenta MCP.
### Esquema de Configuração
```json
{
"type": "mcp-tool",
"name": "get_bank_balance",
"description": "Get bank account balance",
"mcp-tool": "get_bank_balance",
"arguments": [
{
"name": "account_id",
"type": "string",
"description": "Bank account identifier"
},
{
"name": "date",
"type": "string",
"description": "Date for balance query (optional, format: YYYY-MM-DD)"
}
]
}
```
### Fluxo de Dados
1. **Carregamento de Configuração**: A configuração da ferramenta MCP, juntamente com seus argumentos, é carregada por `on_tools_config()`
2. **Criação da Ferramenta**: Os argumentos são analisados e passados para `McpToolImpl` através do construtor
3. **Geração de Prompt**: `agent_manager.py` chama `tool.arguments` para incluir nos prompts do LLM
4. **Invocar a Ferramenta**: O LLM fornece parâmetros que são passados para o serviço MCP sem modificação
### Alterações na API
Não há alterações na API externa - isso é puramente configuração interna e tratamento de argumentos.
### Detalhes do Componente
#### Componente 1: service.py (Carregamento de Configuração da Ferramenta)
**Propósito**: Analisar as configurações da ferramenta MCP e criar instâncias da ferramenta
**Alterações Necessárias**: Adicionar análise de argumentos para ferramentas MCP (semelhante às ferramentas de prompt)
**Nova Funcionalidade**: Extrair o array `arguments` da configuração da ferramenta MCP e criar objetos `Argument`
#### Componente 2: tools.py (McpToolImpl)
**Propósito**: Wrapper de implementação da ferramenta MCP
**Alterações Necessárias**: Aceitar argumentos no construtor e retorná-los de `get_arguments()`
**Nova Funcionalidade**: Armazenar e expor os argumentos configurados em vez de retornar uma lista vazia
#### Componente 3: Workbench (Repositório Externo)
**Propósito**: Interface do usuário para configurar ferramentas de agente
**Alterações Necessárias**: Adicionar interface do usuário para especificar argumentos para ferramentas MCP
**Nova Funcionalidade**: Permitir que os usuários adicionem/editem/removam argumentos para ferramentas MCP
#### Componente 4: Ferramentas de Linha de Comando
**Propósito**: Gerenciamento de ferramentas de linha de comando
**Alterações Necessárias**: Suportar a especificação de argumentos nos comandos de criação/atualização da ferramenta MCP
**Nova Funcionalidade**: Aceitar o parâmetro de argumentos nos comandos de configuração da ferramenta
## Plano de Implementação
### Fase 1: Alterações no Framework Central do Agente
[ ] Atualizar o construtor de `McpToolImpl` para aceitar o parâmetro `arguments`
[ ] Alterar `McpToolImpl.get_arguments()` para retornar os argumentos armazenados
[ ] Modificar a análise de configuração da ferramenta MCP em `service.py` para lidar com argumentos
[ ] Adicionar testes unitários para o tratamento de argumentos da ferramenta MCP
[ ] Verificar se os prompts do agente incluem os argumentos da ferramenta MCP
### Fase 2: Suporte para Ferramentas Externas
[ ] Atualizar as ferramentas de linha de comando para suportar a especificação de argumentos da ferramenta MCP
[ ] Documentar o formato de configuração de argumentos para os usuários
[ ] Atualizar a interface do usuário do Workbench para suportar a configuração de argumentos da ferramenta MCP
[ ] Adicionar exemplos e documentação
### Resumo das Alterações no Código
| Arquivo | Tipo de Alteração | Descrição |
|------|------------|-------------|
| `tools.py` | Modificado | Atualizar McpToolImpl para aceitar e armazenar argumentos |
| `service.py` | Modificado | Analisar argumentos da configuração da ferramenta MCP (linhas 108-113) |
| `test_react_processor.py` | Modificado | Adicionar testes para argumentos da ferramenta MCP |
| Ferramentas de linha de comando | Modificado | Suportar a especificação de argumentos nos comandos |
| Workbench | Modificado | Adicionar interface do usuário para a configuração de argumentos da ferramenta MCP |
## Estratégia de Teste
### Testes Unitários
**Análise de Argumentos da Ferramenta MCP**: Testar se `service.py` analisa corretamente os argumentos das configurações da ferramenta MCP
**Argumentos do McpToolImpl**: Testar se `get_arguments()` retorna os argumentos configurados em vez de uma lista vazia
**Compatibilidade com Versões Anteriores**: Testar se as ferramentas MCP sem argumentos continuam a funcionar (retornam uma lista vazia)
**Geração de Prompt do Agente**: Testar se os prompts do agente incluem os detalhes dos argumentos da ferramenta MCP
### Testes de Integração
**Invocar Ferramenta de Extremo a Extremo**: O agente de teste com argumentos da ferramenta MCP pode invocar ferramentas com sucesso.
**Carregamento de Configuração**: Testar o ciclo completo de carregamento de configuração com argumentos da ferramenta MCP.
**Componentes Cruzados**: Testar se os argumentos fluem corretamente de config → criação de ferramenta → geração de prompt.
### Testes Manuais
**Comportamento do Agente**: Verificar manualmente se o LLM recebe e usa as informações dos argumentos nos ciclos ReACT.
**Integração com a Linha de Comando**: Testar se tg-invoke-mcp-tool funciona com ferramentas MCP configuradas com novos argumentos.
**Integração com o Workbench**: Testar se a interface do usuário suporta a configuração de argumentos para ferramentas MCP.
## Migração e Implantação
### Estratégia de Migração
Não é necessária migração - esta é uma funcionalidade puramente adicional:
As configurações existentes da ferramenta MCP sem `arguments` continuam a funcionar inalteradas.
`McpToolImpl.get_arguments()` retorna uma lista vazia para ferramentas legadas.
Novas configurações podem incluir opcionalmente o array `arguments`.
### Plano de Implantação
1. **Fase 1**: Implantar as alterações principais do framework do agente para desenvolvimento/staging.
2. **Fase 2**: Implantar as atualizações da ferramenta de linha de comando e a documentação.
3. **Fase 3**: Implantar as atualizações da interface do usuário do Workbench para a configuração de argumentos.
4. **Fase 4**: Implantação em produção com monitoramento.
### Plano de Reversão
As alterações principais são compatíveis com versões anteriores - nenhuma reversão é necessária para a funcionalidade.
Se surgirem problemas, desative a análise de argumentos revertendo a lógica de carregamento da configuração da ferramenta MCP.
As alterações do Workbench e da linha de comando são independentes e podem ser revertidas separadamente.
## Considerações de Segurança
**Nenhuma nova superfície de ataque**: Os argumentos são analisados a partir de fontes de configuração existentes, sem novas entradas.
**Validação de parâmetros**: Os argumentos são passados para as ferramentas MCP sem alterações - a validação permanece no nível da ferramenta MCP.
**Integridade da configuração**: As especificações de argumentos fazem parte da configuração da ferramenta - o mesmo modelo de segurança se aplica.
## Impacto no Desempenho
**Sobrecarga mínima**: A análise de argumentos ocorre apenas durante o carregamento da configuração, não por solicitação.
**Aumento do tamanho do prompt**: Os prompts do agente incluirão detalhes dos argumentos da ferramenta MCP, aumentando ligeiramente o uso de tokens.
**Uso de memória**: Aumento insignificante para armazenar as especificações de argumentos em objetos de ferramenta.
## Documentação
### Documentação do Usuário
[ ] Atualizar o guia de configuração da ferramenta MCP com exemplos de argumentos.
[ ] Adicionar a especificação de argumentos ao texto de ajuda da ferramenta de linha de comando.
[ ] Criar exemplos de padrões comuns de argumentos da ferramenta MCP.
### Documentação para Desenvolvedores
[ ] Atualizar a documentação da classe McpToolImpl.
[ ] Adicionar comentários inline para a lógica de análise de argumentos.
[ ] Documentar o fluxo de argumentos na arquitetura do sistema.
## Perguntas Abertas
1. **Validação de argumentos**: Devemos validar os tipos/formatos de argumentos além da verificação básica da estrutura?
2. **Descoberta dinâmica**: Uma melhoria futura para consultar os servidores MCP para esquemas de ferramentas automaticamente?
## Alternativas Consideradas
1. **Descoberta dinâmica do esquema MCP**: Consultar os servidores MCP para esquemas de argumentos em tempo de execução - rejeitado devido à complexidade e preocupações com a confiabilidade.
2. **Registro de argumentos separado**: Armazenar os argumentos da ferramenta MCP em uma seção de configuração separada - rejeitado devido à inconsistência com a abordagem do modelo de prompt.
3. **Validação de tipo**: Validação completa do esquema JSON para argumentos - adiada como uma melhoria futura para manter a implementação inicial simples.
## Referências
[Especificação do Protocolo MCP](https://github.com/modelcontextprotocol/spec)
[Implementação da Ferramenta do Modelo de Prompt](./trustgraph-flow/trustgraph/agent/react/service.py#L114-129)
[Implementação Atual da Ferramenta MCP](./trustgraph-flow/trustgraph/agent/react/tools.py#L58-86)
## Apêndice
[Quaisquer informações adicionais, diagramas ou exemplos]

View file

@ -0,0 +1,562 @@
---
layout: default
title: "Especificação de Autenticação de Token Bearer para Ferramentas MCP"
parent: "Portuguese (Beta)"
---
# Especificação de Autenticação de Token Bearer para Ferramentas 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: APENAS PARA AMBIENTES DE ÚNICO LOCATÁRIO**
>
> Esta especificação descreve um mecanismo de autenticação básico, de nível de serviço, para ferramentas MCP. Não é uma solução de autenticação completa e não é adequada para:
> - Ambientes multiusuário
> - Implantações multi-tenant
> - Autenticação federada
> - Propagação do contexto do usuário
> - Autorização por usuário
>
> Este recurso fornece **um token estático por ferramenta MCP**, compartilhado entre todos os usuários e sessões. Se você precisar de autenticação por usuário ou por tenant, esta não é a solução certa.
## Visão Geral
**Nome do Recurso**: Suporte para Autenticação de Token Bearer para Ferramentas MCP
**Autor**: Claude Code Assistant
**Data**: 2025-11-11
**Status**: Em Desenvolvimento
### Resumo Executivo
Permite que as configurações de ferramentas MCP especifiquem tokens bearer opcionais para autenticar com servidores MCP protegidos. Isso permite que o TrustGraph invoque ferramentas MCP hospedadas em servidores que exigem autenticação, sem modificar as interfaces de invocação de agentes ou ferramentas.
**IMPORTANTE**: Este é um mecanismo de autenticação básico projetado para cenários de autenticação de serviço para serviço, de único tenant. Não é adequado para:
Ambientes multiusuário onde diferentes usuários precisam de credenciais diferentes
Implantações multi-tenant que requerem isolamento por tenant
Cenários de autenticação federada
Autenticação ou autorização em nível de usuário
Gerenciamento de credenciais dinâmico ou atualização de tokens
Este recurso fornece um token bearer estático e de sistema amplo por configuração de ferramenta MCP, compartilhado entre todos os usuários e invocações dessa ferramenta.
### Declaração do Problema
Atualmente, as ferramentas MCP só podem se conectar a servidores MCP acessíveis publicamente. Muitas implantações de produção de MCP exigem autenticação por meio de tokens bearer para segurança. Sem suporte de autenticação:
As ferramentas MCP não podem se conectar a servidores MCP protegidos
Os usuários devem expor servidores MCP publicamente ou implementar proxies reversos
Não há uma maneira padronizada de passar credenciais para conexões MCP
As melhores práticas de segurança não podem ser aplicadas aos endpoints MCP
### Objetivos
[ ] Permitir que as configurações de ferramentas MCP especifiquem um parâmetro opcional `auth-token`
[ ] Atualizar o serviço de ferramenta MCP para usar tokens bearer ao se conectar a servidores MCP
[ ] Atualizar as ferramentas de linha de comando para suportar a configuração/exibição de tokens de autenticação
[ ] Manter a compatibilidade com versões anteriores com configurações MCP sem autenticação
[ ] Documentar as considerações de segurança para o armazenamento de tokens
### Não Objetivos
Atualização dinâmica de tokens ou fluxos OAuth (apenas tokens estáticos)
Criptografia de tokens armazenados (a segurança do sistema de configuração está fora do escopo)
Métodos de autenticação alternativos (autenticação básica, chaves de API, etc.)
Validação ou verificação de expiração de tokens
**Autenticação por usuário**: Este recurso NÃO suporta credenciais específicas do usuário
**Isolamento multi-tenant**: Este recurso NÃO fornece gerenciamento de tokens por tenant
**Autenticação federada**: Este recurso NÃO se integra com provedores de identidade (SSO, OAuth, SAML, etc.)
**Autenticação com contexto**: Os tokens não são passados com base no contexto do usuário ou na sessão
## Contexto e Informações Adicionais
### Estado Atual
As configurações de ferramentas MCP são armazenadas no grupo de configuração `mcp` com esta estrutura:
```json
{
"remote-name": "tool_name",
"url": "http://mcp-server:3000/api"
}
```
O serviço da ferramenta MCP conecta-se a servidores usando `streamablehttp_client(url)` sem nenhum cabeçalho de autenticação.
### Limitações
**Limitações do Sistema Atual:**
1. **Sem suporte para autenticação**: Não é possível conectar-se a servidores MCP protegidos.
2. **Exposição de segurança**: Os servidores MCP devem ser acessíveis publicamente ou usar apenas segurança em nível de rede.
3. **Problemas de implantação em produção**: Não é possível seguir as melhores práticas de segurança para endpoints de API.
**Limitações desta Solução:**
1. **Apenas para um único tenant**: Um token estático por ferramenta MCP, compartilhado entre todos os usuários.
2. **Sem credenciais por usuário**: Não é possível autenticar-se como diferentes usuários ou passar o contexto do usuário.
3. **Sem suporte para multi-tenant**: Não é possível isolar as credenciais por tenant ou organização.
4. **Apenas tokens estáticos**: Sem suporte para atualização, rotação ou tratamento de expiração de tokens.
5. **Autenticação em nível de serviço**: Autentica o serviço TrustGraph, não usuários individuais.
6. **Contexto de segurança compartilhado**: Todas as invocações de uma ferramenta MCP usam a mesma credencial.
### Aplicabilidade do Caso de Uso
**✅ Casos de Uso Adequados:**
Implantações TrustGraph de um único tenant.
Autenticação de serviço para serviço (TrustGraph → Servidor MCP).
Ambientes de desenvolvimento e teste.
Ferramentas MCP internas acessadas pelo sistema TrustGraph.
Cenários em que todos os usuários compartilham o mesmo nível de acesso à ferramenta MCP.
Credenciais de serviço estáticas e de longa duração.
**❌ Casos de Uso Inadequados:**
Sistemas multiusuário que requerem autenticação por usuário.
Implantações SaaS multi-tenant com requisitos de isolamento de tenant.
Cenários de autenticação federada (SSO, OAuth, SAML).
Sistemas que requerem a propagação do contexto do usuário para os servidores MCP.
Ambientes que precisam de atualização dinâmica de tokens ou tokens de curta duração.
Aplicações em que diferentes usuários precisam de diferentes níveis de permissão.
Requisitos de conformidade para rastreamento de auditoria em nível de usuário.
**Exemplo de Cenário Adequado:**
Uma implantação TrustGraph de uma única organização em que todos os funcionários usam a mesma ferramenta MCP interna (por exemplo, consulta de banco de dados da empresa). O servidor MCP requer autenticação para evitar acesso externo, mas todos os usuários internos têm o mesmo nível de acesso.
**Exemplo de Cenário Inadequado:**
Uma plataforma SaaS TrustGraph multi-tenant em que o Tenant A e o Tenant B precisam acessar seus próprios servidores MCP isolados com credenciais separadas. Este recurso NÃO suporta o gerenciamento de tokens por tenant.
### Componentes Relacionados
**trustgraph-flow/trustgraph/agent/mcp_tool/service.py**: Serviço de invocação da ferramenta MCP.
**trustgraph-cli/trustgraph/cli/set_mcp_tool.py**: Ferramenta de linha de comando para criar/atualizar configurações de MCP.
**trustgraph-cli/trustgraph/cli/show_mcp_tools.py**: Ferramenta de linha de comando para exibir configurações de MCP.
**SDK Python para MCP**: `streamablehttp_client` de `mcp.client.streamable_http`
## Requisitos
### Requisitos Funcionais
1. **Token de Autenticação da Configuração MCP**: As configurações da ferramenta MCP DEVEM suportar um campo opcional `auth-token`.
2. **Uso do Token Bearer**: O serviço da ferramenta MCP DEVE enviar o cabeçalho `Authorization: Bearer {token}` quando um token de autenticação estiver configurado.
3. **Suporte da CLI**: `tg-set-mcp-tool` DEVE aceitar um parâmetro opcional `--auth-token`.
4. **Exibição do Token**: `tg-show-mcp-tools` DEVE indicar quando um token de autenticação está configurado (mascarado por segurança).
5. **Compatibilidade com versões anteriores**: As configurações de ferramentas MCP existentes sem token de autenticação DEVEM continuar a funcionar.
### Requisitos Não Funcionais
1. **Compatibilidade com versões anteriores**: Nenhuma alteração disruptiva para as configurações de ferramentas MCP existentes.
2. **Desempenho**: Nenhum impacto significativo no desempenho da invocação da ferramenta MCP.
3. **Segurança**: Tokens armazenados na configuração (documentar as implicações de segurança).
### Histórias de Usuário
1. Como um **engenheiro de DevOps**, quero configurar tokens bearer para ferramentas MCP para que eu possa proteger os endpoints do servidor MCP.
2. Como um **usuário da CLI**, quero definir tokens de autenticação ao criar ferramentas MCP para que eu possa conectar-me a servidores protegidos.
3. Como um **administrador do sistema**, quero ver quais ferramentas MCP têm a autenticação configurada para que eu possa auditar as configurações de segurança.
## Design
### Arquitetura de Alto Nível
Estender a configuração e o serviço da ferramenta MCP para suportar a autenticação por token bearer:
1. Adicionar um campo opcional `auth-token` ao esquema de configuração da ferramenta MCP.
2. Modificar o serviço da ferramenta MCP para ler o token de autenticação e passá-lo para o cliente HTTP.
3. Atualizar as ferramentas da CLI para suportar a definição e a exibição de tokens de autenticação.
4. Documentar as considerações de segurança e as melhores práticas.
### Esquema de Configuração
**Esquema Atual**:
```json
{
"remote-name": "tool_name",
"url": "http://mcp-server:3000/api"
}
```
**Novo Esquema** (com token de autenticação opcional):
```json
{
"remote-name": "tool_name",
"url": "http://mcp-server:3000/api",
"auth-token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
**Descrições dos Campos**:
`remote-name` (opcional): Nome usado pelo servidor MCP (o padrão é a chave de configuração).
`url` (obrigatório): URL do endpoint do servidor MCP.
`auth-token` (opcional): Token Bearer para autenticação.
### Fluxo de Dados
1. **Armazenamento de Configuração**: O usuário executa `tg-set-mcp-tool --id my-tool --tool-url http://server/api --auth-token xyz123`.
2. **Carregamento da Configuração**: O serviço da ferramenta MCP recebe a atualização da configuração via callback `on_mcp_config()`.
3. **Invocar a Ferramenta**: Quando a ferramenta é invocada:
O serviço lê `auth-token` da configuração (se presente).
Cria um dicionário de cabeçalhos: `{"Authorization": "Bearer {token}"}`.
Passa os cabeçalhos para `streamablehttp_client(url, headers=headers)`.
O servidor MCP valida o token e processa a solicitação.
### Alterações na API
Nenhuma alteração na API externa - apenas extensão do esquema de configuração.
### Detalhes do Componente
#### Componente 1: service.py (Serviço da Ferramenta MCP)
**Arquivo**: `trustgraph-flow/trustgraph/agent/mcp_tool/service.py`
**Propósito**: Invocar ferramentas MCP em servidores remotos.
**Alterações Necessárias** (no método `invoke_tool()`):
1. Verificar se `auth-token` está presente em `self.mcp_services[name]` config.
2. Criar um dicionário de cabeçalhos com o cabeçalho Authorization, se o token existir.
3. Passar os cabeçalhos para `streamablehttp_client(url, headers=headers)`.
**Código Atual** (linhas 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 (Ferramenta de Configuração de Linha de Comando)
**Arquivo**: `trustgraph-cli/trustgraph/cli/set_mcp_tool.py`
**Propósito**: Criar/atualizar configurações da ferramenta MCP
**Alterações Necessárias**:
1. Adicionar argumento opcional `--auth-token` a argparse
2. Incluir `auth-token` na configuração JSON quando fornecido
**Argumentos Atuais**:
`--id` (obrigatório): Identificador da ferramenta MCP
`--remote-name` (opcional): Nome da ferramenta MCP remota
`--tool-url` (obrigatório): Endpoint da URL da ferramenta MCP
`-u, --api-url` (opcional): URL da API TrustGraph
**Novo Argumento**:
`--auth-token` (opcional): Token Bearer para autenticação
**Construção de Configuração 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 (Ferramenta de Exibição de Linha de Comando)
**Arquivo**: `trustgraph-cli/trustgraph/cli/show_mcp_tools.py`
**Propósito**: Exibir configurações da ferramenta MCP
**Alterações Necessárias**:
1. Adicionar coluna "Auth" à tabela de saída
2. Exibir "Sim" ou "Não" com base na presença de auth-token
3. Não exibir o valor real do token (segurança)
**Saída Atual**:
```
ID Remote Name URL
---------- ------------- ------------------------
my-tool my-tool http://server:3000/api
```
**Nova Saída**:
```
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: Documentação
**Arquivo**: `docs/cli/tg-set-mcp-tool.md`
**Alterações Necessárias**:
1. Documentar o novo parâmetro `--auth-token`
2. Fornecer um exemplo de uso com autenticação
3. Documentar as considerações de segurança
## Plano de Implementação
### Fase 1: Criar Especificação Técnica
[x] Escrever uma especificação técnica abrangente documentando todas as alterações
### Fase 2: Atualizar o Serviço MCP Tool
[ ] Modificar `invoke_tool()` em `service.py` para ler o token de autenticação da configuração
[ ] Criar um dicionário de cabeçalhos e passá-lo para `streamablehttp_client`
[ ] Testar com um servidor MCP autenticado
### Fase 3: Atualizar as Ferramentas CLI
[ ] Adicionar o argumento `--auth-token` a `set_mcp_tool.py`
[ ] Incluir o token de autenticação no arquivo de configuração JSON
[ ] Adicionar a coluna "Auth" à saída de `show_mcp_tools.py`
[ ] Testar as alterações nas ferramentas CLI
### Fase 4: Atualizar a Documentação
[ ] Documentar o parâmetro `--auth-token` em `tg-set-mcp-tool.md`
[ ] Adicionar uma seção de considerações de segurança
[ ] Fornecer um exemplo de uso
### Fase 5: Testes
[ ] Testar se a ferramenta MCP se conecta com sucesso usando o token de autenticação
[ ] Testar a compatibilidade com versões anteriores (ferramentas sem token de autenticação ainda funcionam)
[ ] Testar se as ferramentas CLI aceitam e armazenam corretamente o token de autenticação
[ ] Testar se o comando "show" exibe o status de autenticação corretamente
### Resumo das Alterações no Código
| Arquivo | Tipo de Alteração | Linhas | Descrição |
|------|------------|-------|-------------|
| `service.py` | Modificado | ~52-66 | Adicionar leitura do token de autenticação e construção de cabeçalhos |
| `set_mcp_tool.py` | Modificado | ~30-60 | Adicionar argumento --auth-token e armazenamento na configuração |
| `show_mcp_tools.py` | Modificado | ~40-70 | Adicionar coluna Auth à exibição |
| `tg-set-mcp-tool.md` | Modificado | Várias | Documentar novo parâmetro |
## Estratégia de Testes
### Testes Unitários
**Leitura do Token de Autenticação**: Testar se `invoke_tool()` lê corretamente o token de autenticação da configuração
**Construção de Cabeçalhos**: Testar se o cabeçalho de Autorização é construído corretamente com o prefixo Bearer
**Compatibilidade com Versões Anteriores**: Testar se as ferramentas sem token de autenticação funcionam inalteradas
**Análise de Argumentos da CLI**: Testar se o argumento `--auth-token` é analisado corretamente
### Testes de Integração
**Conexão Autenticada**: Testar se o serviço da ferramenta MCP se conecta a um servidor autenticado
**Teste de Ponta a Ponta**: Testar o fluxo da CLI → armazenamento na configuração → invocação do serviço com o token de autenticação
**Token Não Necessário**: Testar a conexão com um servidor não autenticado ainda funciona
### Testes Manuais
**Servidor MCP Real**: Testar com um servidor MCP real que requer autenticação com token Bearer
**Fluxo de Trabalho da CLI**: Testar o fluxo de trabalho completo: configurar a ferramenta com autenticação → invocar a ferramenta → verificar o sucesso
**Mascaramento de Autenticação**: Verificar se o status de autenticação é exibido, mas o valor do token não é exposto
## Migração e Implantação
### Estratégia de Migração
Não é necessária migração - esta é uma funcionalidade puramente adicional:
As configurações existentes da ferramenta MCP sem `auth-token` continuam a funcionar inalteradas
Novas configurações podem incluir opcionalmente o campo `auth-token`
As ferramentas CLI aceitam, mas não exigem o parâmetro `--auth-token`
### Plano de Implantação
1. **Fase 1**: Implantar as alterações principais do serviço no desenvolvimento/ambiente de teste
2. **Fase 2**: Implantar as atualizações das ferramentas CLI
3. **Fase 3**: Atualizar a documentação
4. **Fase 4**: Implantação em produção com monitoramento
### Plano de Reversão
As alterações principais são compatíveis com versões anteriores - as ferramentas existentes não são afetadas
Se surgirem problemas, o tratamento do token de autenticação pode ser desativado removendo a lógica de construção de cabeçalhos
As alterações da CLI são independentes e podem ser revertidas separadamente
## Considerações de Segurança
### ⚠️ Limitação Crítica: Apenas Autenticação para um Único Inquilino
**Este mecanismo de autenticação NÃO é adequado para ambientes multiusuário ou multi-inquilino.**
**Credenciais compartilhadas**: Todos os usuários e invocações compartilham o mesmo token por ferramenta MCP
**Sem contexto de usuário**: O servidor MCP não pode distinguir entre diferentes usuários do TrustGraph
**Sem isolamento de inquilino**: Todos os inquilinos compartilham a mesma credencial para cada ferramenta MCP
**Limitação do registro de auditoria**: O servidor MCP registra todas as solicitações da mesma credencial
**Escopo de permissão**: Não é possível impor diferentes níveis de permissão para diferentes usuários
**NÃO use este recurso se:**
Seu deployment do TrustGraph atende a várias organizações (multi-inquilino)
Você precisa rastrear qual usuário acessou qual ferramenta MCP
Diferentes usuários precisam de diferentes níveis de permissão
Você precisa cumprir requisitos de auditoria em nível de usuário
Seu servidor MCP impõe limites de taxa ou cotas por usuário
**Soluções alternativas para cenários multiusuário/multilocatário:**
Implementar a propagação do contexto do usuário por meio de cabeçalhos personalizados
Implantar instâncias separadas do TrustGraph por locatário
Usar isolamento em nível de rede (VPCs, malhas de serviço)
Implementar uma camada de proxy que lida com a autenticação por usuário
### Armazenamento de Tokens
**Risco**: Tokens de autenticação armazenados em texto simples no sistema de configuração
**Mitigação**:
Documentar que os tokens são armazenados sem criptografia
Recomendar o uso de tokens de curta duração sempre que possível
Recomendar controles de acesso adequados no armazenamento de configuração
Considerar uma melhoria futura para o armazenamento de tokens criptografados
### Exposição de Tokens
**Risco**: Tokens podem ser expostos em logs ou na saída da linha de comando
**Mitigação**:
Não registrar os valores dos tokens (registrar apenas "autenticação configurada: sim/não")
O comando de exibição da linha de comando mostra apenas o status mascarado, não o token real
Não incluir tokens em mensagens de erro
### Segurança da Rede
**Risco**: Tokens transmitidos por conexões não criptografadas
**Mitigação**:
Documentar a recomendação de usar URLs HTTPS para servidores MCP
Alertar os usuários sobre o risco de transmissão em texto simples com HTTP
### Acesso à Configuração
**Risco**: Acesso não autorizado ao sistema de configuração expõe tokens
**Mitigação**:
Documentar a importância de proteger o acesso ao sistema de configuração
Recomendar o princípio do menor privilégio para o acesso à configuração
Considerar o registro de auditoria para alterações na configuração (melhoria futura)
### Ambientes Multiusuário
**Risco**: Em implantações multiusuário, todos os usuários compartilham as mesmas credenciais do MCP
**Entendendo o Risco**:
O usuário A e o usuário B usam o mesmo token ao acessar uma ferramenta MCP
O servidor MCP não consegue distinguir entre diferentes usuários do TrustGraph
Não há como impor permissões ou limites de taxa por usuário
Os logs de auditoria no servidor MCP mostram todos os pedidos da mesma credencial
Se a sessão de um usuário for comprometida, o invasor terá o mesmo acesso ao MCP que todos os usuários
**Isso NÃO é um bug - é uma limitação fundamental deste design.**
## Impacto no Desempenho
**Sobrecarga mínima**: A construção do cabeçalho adiciona um tempo de processamento insignificante
**Impacto na rede**: O cabeçalho HTTP adicional adiciona ~50-200 bytes por solicitação
**Uso de memória**: Aumento insignificante para armazenar a string do token na configuração
## Documentação
### Documentação do Usuário
[ ] Atualizar `tg-set-mcp-tool.md` com o parâmetro `--auth-token`
[ ] Adicionar seção de considerações de segurança
[ ] Fornecer exemplo de uso com token de portador
[ ] Documentar as implicações do armazenamento de tokens
### Documentação para Desenvolvedores
[ ] Adicionar comentários inline para o tratamento de tokens de autenticação em `service.py`
[ ] Documentar a lógica de construção de cabeçalhos
[ ] Atualizar a documentação do esquema de configuração da ferramenta MCP
## Perguntas Abertas
1. **Criptografia de tokens**: Devemos implementar o armazenamento de tokens criptografados no sistema de configuração?
2. **Atualização de tokens**: Suporte futuro para fluxos de atualização OAuth ou rotação de tokens?
3. **Métodos de autenticação alternativos**: Devemos suportar autenticação básica, chaves de API ou outros métodos?
## Alternativas Consideradas
1. **Variáveis de ambiente para tokens**: Armazenar tokens em variáveis de ambiente em vez de configuração
**Rejeitado**: Complica o gerenciamento de implantação e configuração
2. **Armazenamento de segredos separado**: Usar um sistema dedicado de gerenciamento de segredos
**Adiado**: Fora do escopo da implementação inicial, considerar uma melhoria futura
3. **Múltiplos métodos de autenticação**: Suportar Basic, API key, OAuth, etc.
**Rejeitado**: Tokens de portador cobrem a maioria dos casos de uso, manter a implementação inicial simples
4. **Armazenamento de tokens criptografado**: Criptografar tokens no sistema de configuração
**Adiado**: A segurança do sistema de configuração é uma preocupação mais ampla, adiar para trabalhos futuros
5. **Tokens por invocação**: Permitir que os tokens sejam passados no momento da invocação
**Rejeitado**: Viola a separação de responsabilidades, o agente não deve lidar com credenciais
## Referências
[Especificação do Protocolo MCP](https://github.com/modelcontextprotocol/spec)
[Autenticação Bearer HTTP (RFC 6750)](https://tools.ietf.org/html/rfc6750)
[Serviço da Ferramenta MCP Atual](../trustgraph-flow/trustgraph/agent/mcp_tool/service.py)
[Especificação de Argumentos da Ferramenta MCP](./mcp-tool-arguments.md)
## Apêndice
### Exemplo de Uso
**Configuração da ferramenta MCP com autenticação**:
```bash
tg-set-mcp-tool \
--id secure-tool \
--tool-url https://secure-server.example.com/mcp \
--auth-token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Exibindo ferramentas 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
```
### Exemplo de Configuração
**Armazenado no sistema de configuração**:
```json
{
"type": "mcp",
"key": "secure-tool",
"value": "{\"url\": \"https://secure-server.example.com/mcp\", \"auth-token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"}"
}
```
### Melhores Práticas de Segurança
1. **Use HTTPS**: Utilize sempre URLs HTTPS para servidores MCP com autenticação.
2. **Tokens de curta duração**: Utilize tokens com data de expiração sempre que possível.
3. **Privilégios mínimos**: Conceda aos tokens as permissões mínimas necessárias.
4. **Controle de acesso**: Restrinja o acesso ao sistema de configuração.
5. **Rotação de tokens**: Rotacione os tokens regularmente.
6. **Registro de auditoria**: Monitore as alterações de configuração para eventos de segurança.

View file

@ -0,0 +1,266 @@
---
layout: default
title: "Especificação Técnica: Suporte a Backend de Armazenamento Compatível com S3"
parent: "Portuguese (Beta)"
---
# Especificação Técnica: Suporte a Backend de Armazenamento Compatível com 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.
## Visão Geral
O serviço Librarian utiliza armazenamento de objetos compatível com S3 para o armazenamento de blobs de documentos. Esta especificação documenta a implementação que permite o suporte a qualquer backend compatível com S3, incluindo MinIO, Ceph RADOS Gateway (RGW), AWS S3, Cloudflare R2, DigitalOcean Spaces e outros.
## Arquitetura
### Componentes de Armazenamento
**Armazenamento de Blobs**: Armazenamento de objetos compatível com S3 através da biblioteca cliente `minio` Python.
**Armazenamento de Metadados**: Cassandra (armazena o mapeamento object_id e os metadados do documento).
**Componente Afetado**: Apenas o serviço Librarian.
**Padrão de Armazenamento**: Armazenamento híbrido com metadados no Cassandra e conteúdo no armazenamento compatível com S3.
### Implementação
**Biblioteca**: Cliente `minio` Python (suporta qualquer API compatível com S3).
**Localização**: `trustgraph-flow/trustgraph/librarian/blob_store.py`
**Operações**:
`add()` - Armazenar blob com object_id UUID.
`get()` - Recuperar blob por object_id.
`remove()` - Excluir blob por object_id.
`ensure_bucket()` - Criar bucket se não existir.
**Bucket**: `library`
**Caminho do Objeto**: `doc/{object_id}`
**Tipos MIME Suportados**: `text/plain`, `application/pdf`
### Arquivos Chave
1. `trustgraph-flow/trustgraph/librarian/blob_store.py` - Implementação do BlobStore.
2. `trustgraph-flow/trustgraph/librarian/librarian.py` - Inicialização do BlobStore.
3. `trustgraph-flow/trustgraph/librarian/service.py` - Configuração do serviço.
4. `trustgraph-flow/pyproject.toml` - Dependências (pacote `minio`).
5. `docs/apis/api-librarian.md` - Documentação da API.
## Backends de Armazenamento Suportados
A implementação funciona com qualquer sistema de armazenamento de objetos compatível com S3:
### Testados/Suportados
**Ceph RADOS Gateway (RGW)** - Sistema de armazenamento distribuído com API S3 (configuração padrão).
**MinIO** - Armazenamento de objetos auto-hospedado leve.
**Garage** - Armazenamento S3-compatível geo-distribuído leve.
### Deve Funcionar (Compatível com S3)
**AWS S3** - Armazenamento de objetos em nuvem da Amazon.
**Cloudflare R2** - Armazenamento S3-compatível da Cloudflare.
**DigitalOcean Spaces** - Armazenamento de objetos da DigitalOcean.
**Wasabi** - Armazenamento em nuvem S3-compatível.
**Backblaze B2** - Armazenamento de backup S3-compatível.
Qualquer outro serviço que implemente a API REST S3.
## Configuração
### Argumentos da Linha de Comando
```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>]
```
**Observação:** Não inclua `http://` ou `https://` no endpoint. Use `--object-store-use-ssl` para habilitar HTTPS.
### Variáveis de Ambiente (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
```
### Exemplos
**Ceph RADOS Gateway (padrão):**
```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
```
**Armazenamento (compatível com S3):**
```bash
--object-store-endpoint garage:3900 \
--object-store-access-key GK000000000000000000000001 \
--object-store-secret-key b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427
```
**AWS S3 com 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
```
## Autenticação
Todos os backends compatíveis com S3 requerem a autenticação AWS Signature Version 4 (ou v2):
**Chave de Acesso** - Identificador público (como nome de usuário)
**Chave Secreta** - Chave de assinatura privada (como senha)
O cliente Python da MinIO gerencia automaticamente todos os cálculos de assinatura.
### Criando Credenciais
**Para MinIO:**
```bash
# Use default credentials or create user via MinIO Console
minioadmin / minioadmin
```
**Para o Ceph RGW:**
```bash
radosgw-admin user create --uid="trustgraph" --display-name="TrustGraph Service"
# Returns access_key and secret_key
```
**Para AWS S3:**
Crie um usuário IAM com permissões S3
Gere uma chave de acesso no Console da AWS
## Seleção da Biblioteca: Cliente Python MinIO
**Justificativa:**
Leve (~500KB vs ~50MB do boto3)
Compatível com S3 - funciona com qualquer endpoint de API S3
API mais simples que o boto3 para operações básicas
Já em uso, não é necessária migração
Testado em batalha com MinIO e outros sistemas S3
## Implementação do BlobStore
**Localização:** `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()
```
## Benefícios Principais
1. **Sem Dependência de Fornecedor** - Funciona com qualquer armazenamento compatível com S3.
2. **Leve** - O cliente MinIO tem apenas ~500KB.
3. **Configuração Simples** - Apenas endpoint e credenciais.
4. **Sem Migração de Dados** - Substituição direta entre backends.
5. **Testado em Combate** - O cliente MinIO funciona com todas as principais implementações S3.
## Status da Implementação
Todo o código foi atualizado para usar nomes de parâmetros S3 genéricos:
`blob_store.py` - Atualizado para aceitar `endpoint`, `access_key`, `secret_key`.
`librarian.py` - Nomes de parâmetros atualizados.
`service.py` - Argumentos da CLI e configuração atualizados.
✅ Documentação atualizada.
## Melhorias Futuras
1. **Suporte SSL/TLS** - Adicionar a flag `--s3-use-ssl` para HTTPS.
2. **Lógica de Repetição** - Implementar retrocesso exponencial para falhas transitórias.
3. **URLs Pré-assinadas** - Gerar URLs temporárias de upload/download.
4. **Suporte Multi-região** - Replicar blobs entre regiões.
5. **Integração CDN** - Servir blobs via CDN.
6. **Classes de Armazenamento** - Usar classes de armazenamento S3 para otimização de custos.
7. **Políticas de Ciclo de Vida** - Arquivamento/exclusão automática.
8. **Versionamento** - Armazenar múltiplas versões de blobs.
## Referências
Cliente Python MinIO: https://min.io/docs/minio/linux/developers/python/API.html
API S3 Ceph RGW: https://docs.ceph.com/en/latest/radosgw/s3/
Referência da API S3: https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html

View file

@ -0,0 +1,287 @@
---
layout: default
title: "Mais Configurações - Especificação Técnica da Interface de Linha de Comando (CLI)"
parent: "Portuguese (Beta)"
---
# Mais Configurações - Especificação Técnica da Interface de Linha de Comando (CLI)
> **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.
## Visão Geral
Esta especificação descreve as capacidades aprimoradas de configuração por meio da interface de linha de comando para o TrustGraph, permitindo que os usuários gerenciem itens de configuração individuais por meio de comandos de linha de comando granulares. A integração suporta quatro casos de uso primários:
1. **Listar Itens de Configuração**: Exibir chaves de configuração de um tipo específico
2. **Obter Item de Configuração**: Recuperar valores de configuração específicos
3. **Definir Item de Configuração**: Definir ou atualizar itens de configuração individuais
4. **Excluir Item de Configuração**: Remover itens de configuração específicos
## Objetivos
**Controle Granular**: Permitir o gerenciamento de itens de configuração individuais, em vez de operações em lote
**Listagem Baseada em Tipo**: Permitir que os usuários explorem itens de configuração por tipo
**Operações de Item Único**: Fornecer comandos para obter/definir/excluir itens de configuração individuais
**Integração com a API**: Utilizar a API de Configuração existente para todas as operações
**Padrão de CLI Consistente**: Seguir as convenções e padrões de CLI do TrustGraph estabelecidos
**Tratamento de Erros**: Fornecer mensagens de erro claras para operações inválidas
**Saída JSON**: Suportar saída estruturada para uso programático
**Documentação**: Incluir ajuda abrangente e exemplos de uso
## Contexto
Atualmente, o TrustGraph fornece gerenciamento de configuração por meio da API de Configuração e um único comando de linha de comando `tg-show-config` que exibe toda a configuração. Embora isso funcione para visualizar a configuração, ele carece de capacidades de gerenciamento granular.
As limitações atuais incluem:
Não há como listar itens de configuração por tipo a partir da linha de comando
Não há comando de linha de comando para recuperar valores de configuração específicos
Não há comando de linha de comando para definir itens de configuração individuais
Não há comando de linha de comando para excluir itens de configuração específicos
Esta especificação aborda essas lacunas, adicionando quatro novos comandos de linha de comando que fornecem gerenciamento de configuração granular. Ao expor operações individuais da API de Configuração por meio de comandos de linha de comando, o TrustGraph pode:
Permitir o gerenciamento de configuração por script
Permitir a exploração da estrutura de configuração por tipo
Suportar atualizações de configuração direcionadas
Fornecer controle de configuração granular
## Design Técnico
### Arquitetura
A configuração aprimorada da linha de comando requer os seguintes componentes técnicos:
1. **tg-list-config-items**
Lista chaves de configuração para um tipo especificado
Chama o método da API Config.list(type)
Exibe a lista de chaves de configuração
Módulo: `trustgraph.cli.list_config_items`
2. **tg-get-config-item**
Recupera um(s) item(ns) de configuração específico(s)
Chama o método da API Config.get(keys)
Exibe os valores de configuração em formato JSON
Módulo: `trustgraph.cli.get_config_item`
3. **tg-put-config-item**
Define ou atualiza um item de configuração
Chama o método da API Config.put(values)
Aceita parâmetros de tipo, chave e valor
Módulo: `trustgraph.cli.put_config_item`
4. **tg-delete-config-item**
Remove um item de configuração
Chama o método da API Config.delete(keys)
Aceita parâmetros de tipo e chave
Módulo: `trustgraph.cli.delete_config_item`
### Modelos de Dados
#### ConfigKey e ConfigValue
Os comandos utilizam as estruturas de dados existentes de `trustgraph.api.types`:
```python
@dataclasses.dataclass
class ConfigKey:
type : str
key : str
@dataclasses.dataclass
class ConfigValue:
type : str
key : str
value : str
```
Esta abordagem permite:
Tratamento de dados consistente entre a interface de linha de comando (CLI) e a API.
Operações de configuração com segurança de tipo.
Formatos de entrada/saída estruturados.
Integração com a API de Configuração existente.
### Especificações de comandos da CLI
#### tg-list-config-items
```bash
tg-list-config-items --type <config-type> [--format text|json] [--api-url <url>]
```
**Propósito**: Listar todas as chaves de configuração para um determinado tipo.
**Chamada da API**: `Config.list(type)`
**Saída**:
`text` (padrão): Chaves de configuração separadas por quebras de linha.
`json`: Array JSON de chaves de configuração.
#### tg-get-config-item
```bash
tg-get-config-item --type <type> --key <key> [--format text|json] [--api-url <url>]
```
**Propósito**: Recuperar um item de configuração específico.
**Chamada da API**: `Config.get([ConfigKey(type, key)])`
**Saída**:
`text` (padrão): Valor de string bruto.
`json`: Valor de string codificado em JSON.
#### tg-put-config-item
```bash
tg-put-config-item --type <type> --key <key> --value <value> [--api-url <url>]
tg-put-config-item --type <type> --key <key> --stdin [--api-url <url>]
```
**Propósito**: Definir ou atualizar um item de configuração.
**Chamada da API**: `Config.put([ConfigValue(type, key, value)])`
**Opções de Entrada**:
`--value`: Valor de string fornecido diretamente na linha de comando.
`--stdin`: Ler o valor da entrada padrão.
**Saída**: Confirmação de sucesso.
#### tg-delete-config-item
```bash
tg-delete-config-item --type <type> --key <key> [--api-url <url>]
```
**Propósito**: Excluir item de configuração
**Chamada da API**: `Config.delete([ConfigKey(type, key)])`
**Saída**: Confirmação de sucesso
### Detalhes da Implementação
Todos os comandos seguem o padrão estabelecido da CLI TrustGraph:
Use `argparse` para análise de argumentos da linha de comando
Importe e use `trustgraph.api.Api` para comunicação com o backend
Siga os mesmos padrões de tratamento de erros dos comandos da CLI existentes
Suporte ao parâmetro padrão `--api-url` para configuração do endpoint da API
Forneça texto de ajuda descritivo e exemplos de uso
#### Tratamento do Formato de Saída
**Formato de Texto (Padrão)**:
`tg-list-config-items`: Uma chave por linha, texto simples
`tg-get-config-item`: Valor de string bruto, sem aspas ou codificação
**Formato JSON**:
`tg-list-config-items`: Array de strings `["key1", "key2", "key3"]`
`tg-get-config-item`: Valor de string codificado em JSON `"actual string value"`
#### Tratamento da Entrada
**tg-put-config-item** suporta dois métodos de entrada mutuamente exclusivos:
`--value <string>`: Valor de string direto na linha de comando
`--stdin`: Leia toda a entrada da entrada padrão como o valor de configuração
O conteúdo da entrada padrão é lido como texto bruto (preservando novas linhas, espaços em branco, etc.)
Suporta o envio por pipe de arquivos, comandos ou entrada interativa
## Considerações de Segurança
**Validação da Entrada**: Todos os parâmetros da linha de comando devem ser validados antes das chamadas da API
**Autenticação da API**: Os comandos herdam os mecanismos de autenticação da API existentes
**Acesso à Configuração**: Os comandos respeitam os controles de acesso à configuração existentes
**Informações de Erro**: As mensagens de erro não devem vazar detalhes confidenciais da configuração
## Considerações de Desempenho
**Operações de Item Único**: Os comandos são projetados para itens individuais, evitando a sobrecarga de operações em lote
**Eficiência da API**: As chamadas diretas da API minimizam as camadas de processamento
**Latência da Rede**: Cada comando faz uma chamada de API, minimizando as viagens de ida e volta na rede
**Uso de Memória**: Pegada de memória mínima para operações de item único
## Estratégia de Teste
**Testes Unitários**: Teste cada módulo de comando da CLI independentemente
**Testes de Integração**: Teste os comandos da CLI contra a API de Configuração em tempo real
**Testes de Tratamento de Erros**: Verifique o tratamento adequado de erros para entradas inválidas
**Compatibilidade da API**: Garanta que os comandos funcionem com as versões existentes da API de Configuração
## Plano de Migração
Nenhuma migração necessária - estes são novos comandos da CLI que complementam a funcionalidade existente:
O comando `tg-show-config` existente permanece inalterado
Novos comandos podem ser adicionados incrementalmente
Nenhuma alteração disruptiva nos fluxos de trabalho de configuração existentes
## Empacotamento e Distribuição
Estes comandos serão adicionados ao pacote `trustgraph-cli` existente:
**Localização do Pacote**: `trustgraph-cli/`
**Arquivos do Módulo**:
`trustgraph-cli/trustgraph/cli/list_config_items.py`
`trustgraph-cli/trustgraph/cli/get_config_item.py`
`trustgraph-cli/trustgraph/cli/put_config_item.py`
`trustgraph-cli/trustgraph/cli/delete_config_item.py`
**Pontos de Entrada**: Adicionados a `trustgraph-cli/pyproject.toml` na seção `[project.scripts]`:
```toml
tg-list-config-items = "trustgraph.cli.list_config_items:main"
tg-get-config-item = "trustgraph.cli.get_config_item:main"
tg-put-config-item = "trustgraph.cli.put_config_item:main"
tg-delete-config-item = "trustgraph.cli.delete_config_item:main"
```
## Tarefas de Implementação
1. **Criar Módulos CLI**: Implementar os quatro módulos de comando CLI em `trustgraph-cli/trustgraph/cli/`
2. **Atualizar pyproject.toml**: Adicionar novos pontos de entrada de comando em `trustgraph-cli/pyproject.toml`
3. **Documentação**: Criar documentação CLI para cada comando em `docs/cli/`
4. **Testes**: Implementar cobertura de testes abrangente
5. **Integração**: Garantir que os comandos funcionem com a infraestrutura TrustGraph existente
6. **Construção do Pacote**: Verificar se os comandos são instalados corretamente com `pip install trustgraph-cli`
## Exemplos de Uso
#### Listar itens de configuração
```bash
# List prompt keys (text format)
tg-list-config-items --type prompt
template-1
template-2
system-prompt
# List prompt keys (JSON format)
tg-list-config-items --type prompt --format json
["template-1", "template-2", "system-prompt"]
```
#### Obter item de configuração
```bash
# Get prompt value (text format)
tg-get-config-item --type prompt --key template-1
You are a helpful assistant. Please respond to: {query}
# Get prompt value (JSON format)
tg-get-config-item --type prompt --key template-1 --format json
"You are a helpful assistant. Please respond to: {query}"
```
#### Definir item de configuração
```bash
# Set from command line
tg-put-config-item --type prompt --key new-template --value "Custom prompt: {input}"
# Set from file via pipe
cat ./prompt-template.txt | tg-put-config-item --type prompt --key complex-template --stdin
# Set from file via redirect
tg-put-config-item --type prompt --key complex-template --stdin < ./prompt-template.txt
# Set from command output
echo "Generated template: {query}" | tg-put-config-item --type prompt --key auto-template --stdin
```
#### Excluir item de configuração
```bash
tg-delete-config-item --type prompt --key old-template
```
## Perguntas Abertas
Os comandos devem suportar operações em lote (múltiplas chaves) além de itens individuais?
Qual formato de saída deve ser usado para confirmações de sucesso?
Como os tipos de configuração devem ser documentados/descobertos pelos usuários?
## Referências
API de Configuração existente: `trustgraph/api/config.py`
Padrões de CLI: `trustgraph-cli/trustgraph/cli/show_config.py`
Tipos de dados: `trustgraph/api/types.py`

View file

@ -0,0 +1,780 @@
---
layout: default
title: "Especificação Técnica: Suporte para Multi-Tenancy"
parent: "Portuguese (Beta)"
---
# Especificação Técnica: Suporte para Multi-Tenancy
> **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.
## Visão Geral
Habilite implantações multi-tenant corrigindo incompatibilidades de nomes de parâmetros que impedem a personalização da fila e adicionando parametrização do keyspace do Cassandra.
## Contexto da Arquitetura
### Resolução de Filas Baseada em Fluxo
O sistema TrustGraph usa uma **arquitetura baseada em fluxo** para a resolução dinâmica de filas, que suporta inerentemente o multi-tenancy:
As **Definições de Fluxo** são armazenadas no Cassandra e especificam os nomes das filas por meio de definições de interface.
**Os nomes das filas usam modelos** com variáveis `{id}` que são substituídas pelos IDs das instâncias de fluxo.
**Os serviços resolvem dinamicamente as filas** consultando as configurações de fluxo no momento da solicitação.
**Cada tenant pode ter fluxos únicos** com nomes de fila diferentes, proporcionando isolamento.
Exemplo de definição de interface de fluxo:
```json
{
"interfaces": {
"triples-store": "persistent://tg/flow/triples-store:{id}",
"graph-embeddings-store": "persistent://tg/flow/graph-embeddings-store:{id}"
}
}
```
Quando o tenant A inicia o fluxo `tenant-a-prod` e o tenant B inicia o fluxo `tenant-b-prod`, eles automaticamente recebem filas isoladas:
`persistent://tg/flow/triples-store:tenant-a-prod`
`persistent://tg/flow/triples-store:tenant-b-prod`
**Serviços corretamente projetados para multi-tenancy:**
**Knowledge Management (cores)** - Resolve dinamicamente as filas a partir da configuração do fluxo passada nas requisições
**Serviços que precisam de correções:**
🔴 **Config Service** - Incompatibilidade no nome do parâmetro impede a personalização da fila
🔴 **Librarian Service** - Tópicos de gerenciamento de armazenamento codificados (discutido abaixo)
🔴 **Todos os Serviços** - Não é possível personalizar o keyspace do Cassandra
## Declaração do Problema
### Problema #1: Incompatibilidade no Nome do Parâmetro no AsyncProcessor
**CLI define:** `--config-queue` (nomeação pouco clara)
**Argparse converte para:** `config_queue` (no dicionário de parâmetros)
**Código procura por:** `config_push_queue`
**Resultado:** O parâmetro é ignorado, usa o valor padrão de `persistent://tg/config/config`
**Impacto:** Afeta todos os 32+ serviços que herdam do AsyncProcessor
**Bloqueia:** Impossibilita o uso de filas de configuração específicas do tenant em implantações multi-tenant
**Solução:** Renomear o parâmetro da CLI para `--config-push-queue` para maior clareza (alteração disruptiva aceitável, já que o recurso está atualmente com defeito)
### Problema #2: Incompatibilidade no Nome do Parâmetro no Config Service
**CLI define:** `--push-queue` (nomeação ambígua)
**Argparse converte para:** `push_queue` (no dicionário de parâmetros)
**Código procura por:** `config_push_queue`
**Resultado:** O parâmetro é ignorado
**Impacto:** O Config service não pode usar uma fila de push personalizada
**Solução:** Renomear o parâmetro da CLI para `--config-push-queue` para consistência e clareza (alteração disruptiva aceitável)
### Problema #3: Keyspace do Cassandra Codificado
**Atual:** Keyspace codificado como `"config"`, `"knowledge"`, `"librarian"` em vários serviços
**Resultado:** Impossibilita a personalização do keyspace para implantações multi-tenant
**Impacto:** Serviços Config, cores e librarian
**Bloqueia:** Múltiplos tenants não podem usar keyspaces separados do Cassandra
### Problema #4: Arquitetura de Gerenciamento de Coleções ✅ CONCLUÍDO
**Anterior:** Coleções armazenadas no keyspace do librarian do Cassandra em uma tabela de coleções separada
**Anterior:** O librarian usava 4 tópicos de gerenciamento de armazenamento codificados para coordenar a criação/exclusão de coleções:
`vector_storage_management_topic`
`object_storage_management_topic`
`triples_storage_management_topic`
`storage_management_response_topic`
**Problemas (Resolvidos):**
Tópicos codificados não podiam ser personalizados para implantações multi-tenant
Coordenação assíncrona complexa entre o librarian e 4+ serviços de armazenamento
Tabela separada do Cassandra e infraestrutura de gerenciamento
Filas de requisição/resposta não persistentes para operações críticas
**Solução Implementada:** Migrou as coleções para o armazenamento do serviço de configuração, usa push de configuração para distribuição
**Status:** Todos os backends de armazenamento migrados para o padrão `CollectionConfigHandler`
## Solução
Esta especificação aborda os problemas #1, #2, #3 e #4.
### Parte 1: Corrigir Incompatibilidades no Nome do Parâmetro
#### Alteração 1: Classe Base AsyncProcessor - Renomear Parâmetro da CLI
**Arquivo:** `trustgraph-base/trustgraph/base/async_processor.py`
**Linha:** 260-264
**Atual:**
```python
parser.add_argument(
'--config-queue',
default=default_config_queue,
help=f'Config push queue {default_config_queue}',
)
```
**Corrigido:**
```python
parser.add_argument(
'--config-push-queue',
default=default_config_queue,
help=f'Config push queue (default: {default_config_queue})',
)
```
**Justificativa:**
Nomenclatura mais clara e explícita
Coincide com o nome da variável interna `config_push_queue`
Mudança disruptiva aceitável, já que a funcionalidade está atualmente inativa
Não é necessária nenhuma alteração no código em params.get() - ele já procura pelo nome correto
#### Mudança 2: Serviço de Configuração - Renomear Parâmetro da CLI
**Arquivo:** `trustgraph-flow/trustgraph/config/service/service.py`
**Linha:** 276-279
**Atual:**
```python
parser.add_argument(
'--push-queue',
default=default_config_push_queue,
help=f'Config push queue (default: {default_config_push_queue})'
)
```
**Corrigido:**
```python
parser.add_argument(
'--config-push-queue',
default=default_config_push_queue,
help=f'Config push queue (default: {default_config_push_queue})'
)
```
**Justificativa:**
Nomes mais claros - "config-push-queue" é mais explícito do que apenas "push-queue".
Compatível com o nome da variável interna `config_push_queue`.
Consistente com o parâmetro `--config-push-queue` do AsyncProcessor.
Mudança disruptiva aceitável, já que o recurso está atualmente inativo.
Nenhuma alteração de código necessária em params.get() - ele já procura pelo nome correto.
### Parte 2: Adicionar Parametrização do Keyspace do Cassandra
#### Mudança 3: Adicionar Parâmetro de Keyspace ao Módulo cassandra_config
**Arquivo:** `trustgraph-base/trustgraph/base/cassandra_config.py`
**Adicionar argumento de linha de comando** (na função `add_cassandra_args()`):
```python
parser.add_argument(
'--cassandra-keyspace',
default=None,
help='Cassandra keyspace (default: service-specific)'
)
```
**Adicionar suporte para variáveis de ambiente** (na função `resolve_cassandra_config()`):
```python
keyspace = params.get(
"cassandra_keyspace",
os.environ.get("CASSANDRA_KEYSPACE")
)
```
**Atualizar o valor de retorno** de `resolve_cassandra_config()`:
Atualmente retorna: `(hosts, username, password)`
Alterar para retornar: `(hosts, username, password, keyspace)`
**Justificativa:**
Consistente com o padrão de configuração existente do Cassandra
Disponível para todos os serviços via `add_cassandra_args()`
Suporta configuração via linha de comando e variáveis de ambiente
#### Mudança 4: Serviço de Configuração - Usar Keyspace Parametrizados
**Arquivo:** `trustgraph-flow/trustgraph/config/service/service.py`
**Linha 30** - Remover o keyspace codificado:
```python
# DELETE THIS LINE:
keyspace = "config"
```
**Linhas 69-73** - Atualização da resolução da configuração do Cassandra:
**Atual:**
```python
cassandra_host, cassandra_username, cassandra_password = \
resolve_cassandra_config(params)
```
**Corrigido:**
```python
cassandra_host, cassandra_username, cassandra_password, keyspace = \
resolve_cassandra_config(params, default_keyspace="config")
```
**Justificativa:**
Mantém a compatibilidade com versões anteriores, utilizando "config" como padrão.
Permite a substituição através de `--cassandra-keyspace` ou `CASSANDRA_KEYSPACE`.
#### Mudança 5: Cores/Serviço de Conhecimento - Utilizar Chaves de Espaço de Chaves Parametrizadas
**Arquivo:** `trustgraph-flow/trustgraph/cores/service.py`
**Linha 37** - Remover o espaço de chaves codificado:
```python
# DELETE THIS LINE:
keyspace = "knowledge"
```
**Atualização da resolução de configuração do Cassandra** (localização semelhante ao serviço de configuração):
```python
cassandra_host, cassandra_username, cassandra_password, keyspace = \
resolve_cassandra_config(params, default_keyspace="knowledge")
```
#### Mudança 6: Serviço de Bibliotecário - Use Chaves de Espaço de Chaves Parametrizadas
**Arquivo:** `trustgraph-flow/trustgraph/librarian/service.py`
**Linha 51** - Remova a chave de espaço de chaves codificada:
```python
# DELETE THIS LINE:
keyspace = "librarian"
```
**Atualização da resolução de configuração do Cassandra** (localização semelhante ao serviço de configuração):
```python
cassandra_host, cassandra_username, cassandra_password, keyspace = \
resolve_cassandra_config(params, default_keyspace="librarian")
```
### Parte 3: Migrar o Gerenciamento de Coleções para o Serviço de Configuração
#### Visão Geral
Migrar as coleções do keyspace do Cassandra librarian para o armazenamento do serviço de configuração. Isso elimina os tópicos de gerenciamento de armazenamento codificados e simplifica a arquitetura, utilizando o mecanismo de push de configuração existente para distribuição.
#### Arquitetura Atual
```
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
```
#### Nova Arquitetura
```
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
```
#### Mudança 7: Gerenciador de Coleções - Usar API do Serviço de Configuração
**Arquivo:** `trustgraph-flow/trustgraph/librarian/collection_manager.py`
**Remover:**
Uso de `LibraryTableStore` (Linhas 33, 40-41)
Inicialização de produtores de gerenciamento de armazenamento (Linhas 86-140)
Método `on_storage_response` (Linhas 400-430)
Rastreamento de `pending_deletions` (Linhas 57, 90-96 e uso em todo o código)
**Adicionar:**
Cliente do serviço de configuração para chamadas de API (padrão de solicitação/resposta)
**Configuração do 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` (Linhas 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` (Linhas 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` (Linhas 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 Metadados de Coleção:**
Armazenado na tabela de configuração como: `class='collections', key='user:collection'`
O valor é uma CollectionMetadata serializada em JSON (sem campos de timestamp)
Campos: `user`, `collection`, `name`, `description`, `tags`
Exemplo: `class='collections', key='alice:my-docs', value='{"user":"alice","collection":"my-docs","name":"My Documents","description":"...","tags":["work"]}'`
#### Mudança 8: Serviço de Bibliotecário - Remover a Infraestrutura de Gerenciamento de Armazenamento
**Arquivo:** `trustgraph-flow/trustgraph/librarian/service.py`
**Remover:**
Produtores de gerenciamento de armazenamento (Linhas 173-190):
`vector_storage_management_producer`
`object_storage_management_producer`
`triples_storage_management_producer`
Consumidor de resposta de armazenamento (Linhas 192-201)
Manipulador `on_storage_response` (Linhas 467-473)
**Modificar:**
Inicialização do CollectionManager (Linhas 215-224) - remover os parâmetros do produtor de armazenamento
**Observação:** A API externa de coleções permanece inalterada:
`list-collections`
`update-collection`
`delete-collection`
#### Mudança 9: Remover a Tabela de Coleções do LibraryTableStore
**Arquivo:** `trustgraph-flow/trustgraph/tables/library.py`
**Excluir:**
Instrução CREATE da tabela de coleções (Linhas 114-127)
Prepared statements de coleções (Linhas 205-240)
Todos os métodos de coleção (Linhas 578-717):
`ensure_collection_exists`
`list_collections`
`update_collection`
`delete_collection`
`get_collection`
`create_collection`
**Justificativa:**
As coleções agora são armazenadas na tabela de configuração
Mudança disruptiva aceitável - nenhuma migração de dados necessária
Simplifica significativamente o serviço de bibliotecário
#### Mudança 10: Serviços de Armazenamento - Gerenciamento de Coleção Baseado em Configuração ✅ CONCLUÍDO
**Status:** Todos os 11 backends de armazenamento foram migrados para usar `CollectionConfigHandler`.
**Serviços Afetados (11 no total):**
Embeddings de documentos: milvus, pinecone, qdrant
Embeddings de grafos: milvus, pinecone, qdrant
Armazenamento de objetos: cassandra
Armazenamento de triplas: cassandra, falkordb, memgraph, neo4j
**Arquivos:**
`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`
**Padrão de Implementação (todos os serviços):**
1. **Registrar o manipulador de configuração em `__init__`:**
```python
# Add after AsyncProcessor initialization
self.register_config_handler(self.on_collection_config)
self.known_collections = set() # Track (user, collection) tuples
```
2. **Implementar o gerenciador de configuração:**
```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. **Inicialize as coleções conhecidas na inicialização:**
```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. **Refatore os métodos de tratamento 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. **Remover a infraestrutura de gerenciamento de armazenamento:**
Remover a configuração e inicialização de `self.storage_request_consumer`
Remover a configuração de `self.storage_response_producer`
Remover o método de dispatcher de `on_storage_management`
Remover as métricas para o gerenciamento de armazenamento
Remover as importações: `StorageManagementRequest`, `StorageManagementResponse`
**Considerações Específicas para o Backend:**
**Bancos de dados vetoriais (Milvus, Pinecone, Qdrant):** Rastrear a lógica `(user, collection)` em `known_collections`, mas pode criar múltiplas coleções de backend por dimensão. Continuar o padrão de criação preguiçosa. As operações de exclusão devem remover todas as variantes de dimensão.
**Cassandra Objects:** As coleções são propriedades de linha, não estruturas. Rastrear informações no nível do keyspace.
**Bancos de dados de grafos (Neo4j, Memgraph, FalkorDB):** Consultar nós `CollectionMetadata` na inicialização. Criar/excluir nós de metadados na sincronização.
**Cassandra Triples:** Usar a API `KnowledgeGraph` para operações de coleção.
**Pontos-Chave do Design:**
**Consistência eventual:** Não há mecanismo de solicitação/resposta, o envio de configuração é transmitido.
**Idempotência:** Todas as operações de criação/exclusão devem ser seguras para serem repetidas.
**Tratamento de erros:** Registrar erros, mas não bloquear as atualizações de configuração.
**Autorreparação:** As operações com falha serão repetidas na próxima atualização de configuração.
**Formato da chave da coleção:** `"user:collection"` em `config["collections"]`
#### Mudança 11: Atualizar o Esquema da Coleção - Remover Timestamps
**Arquivo:** `trustgraph-base/trustgraph/schema/services/collection.py`
**Modificar CollectionMetadata (Linhas 13-21):**
Remover os campos `created_at` e `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 (linhas 25-47):**
Remover campos de timestamp:
```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()
```
**Justificativa:**
Os carimbos de data e hora não agregam valor para coleções.
O serviço de configuração mantém seu próprio rastreamento de versão.
Simplifica o esquema e reduz o armazenamento.
#### Benefícios da Migração do Serviço de Configuração
1. ✅ **Elimina tópicos de gerenciamento de armazenamento codificados** - Resolve o bloqueio multi-inquilino.
2. ✅ **Coordenação mais simples** - Sem espera assíncrona complexa por 4 ou mais respostas de armazenamento.
3. ✅ **Consistência eventual** - Os serviços de armazenamento são atualizados independentemente por meio de push de configuração.
4. ✅ **Melhor confiabilidade** - Push de configuração persistente versus solicitação/resposta não persistente.
5. ✅ **Modelo de configuração unificado** - Coleções tratadas como configuração.
6. ✅ **Reduz a complexidade** - Remove aproximadamente 300 linhas de código de coordenação.
7. ✅ **Pronto para multi-inquilino** - A configuração já suporta o isolamento de inquilinos por meio de keyspace.
8. ✅ **Rastreamento de versão** - O mecanismo de versão do serviço de configuração fornece um histórico de auditoria.
## Notas de Implementação
### Compatibilidade com versões anteriores
**Alterações de parâmetros:**
As alterações de nome dos parâmetros da CLI são alterações disruptivas, mas aceitáveis (o recurso atualmente não está funcional).
Os serviços funcionam sem parâmetros (use os padrões).
Keyspaces padrão preservados: "config", "knowledge", "librarian".
Fila padrão: `persistent://tg/config/config`
**Gerenciamento de coleções:**
**Alteração disruptiva:** A tabela de coleções foi removida do keyspace librarian.
**Nenhuma migração de dados fornecida** - aceitável para esta fase.
A API de coleção externa não foi alterada (operações de listagem, atualização e exclusão).
O formato de metadados da coleção foi simplificado (os carimbos de data e hora foram removidos).
### Requisitos de teste
**Teste de parâmetros:**
1. Verificar se o parâmetro `--config-push-queue` funciona no serviço graph-embeddings.
2. Verificar se o parâmetro `--config-push-queue` funciona no serviço text-completion.
3. Verificar se o parâmetro `--config-push-queue` funciona no serviço de configuração.
4. Verificar se o parâmetro `--cassandra-keyspace` funciona para o serviço de configuração.
5. Verificar se o parâmetro `--cassandra-keyspace` funciona para o serviço cores.
6. Verificar se o parâmetro `--cassandra-keyspace` funciona para o serviço librarian.
7. Verificar se os serviços funcionam sem parâmetros (usa os padrões).
8. Verificar a implantação multi-inquilino com nomes de fila e keyspace personalizados.
**Teste de gerenciamento de coleções:**
9. Verificar a operação `list-collections` por meio do serviço de configuração.
10. Verificar se `update-collection` cria/atualiza na tabela de configuração.
11. Verificar se `delete-collection` remove da tabela de configuração.
12. Verificar se o push de configuração é acionado em atualizações de coleção.
13. Verificar se a filtragem de tags funciona com armazenamento baseado em configuração.
14. Verificar se as operações de coleção funcionam sem campos de carimbo de data e hora.
### Exemplo de implantação 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álise de Impacto
### Serviços Afetados pela Alteração 1-2 (Renomeação de Parâmetro da CLI)
Todos os serviços que herdam de AsyncProcessor ou FlowProcessor:
config-service
cores-service
librarian-service
graph-embeddings
document-embeddings
text-completion-* (todos os provedores)
extract-* (todos os extractors)
query-* (todos os serviços de consulta)
retrieval-* (todos os serviços RAG)
storage-* (todos os serviços de armazenamento)
E mais 20 serviços
### Serviços Afetados pelas Alterações 3-6 (Keyspace do Cassandra)
config-service
cores-service
librarian-service
### Serviços Afetados pelas Alterações 7-11 (Gerenciamento de Coleções)
**Alterações Imediatas:**
librarian-service (collection_manager.py, service.py)
tables/library.py (remoção da tabela de coleções)
schema/services/collection.py (remoção do timestamp)
**Alterações Concluídas (Alteração 10):** ✅
Todos os serviços de armazenamento (11 no total) - migrados para o push de configuração para atualizações de coleções via `CollectionConfigHandler`
Esquema de gerenciamento de armazenamento removido de `storage.py`
## Considerações Futuras
### Modelo de Keyspace por Usuário
Alguns serviços usam **keyspaces por usuário** dinamicamente, onde cada usuário recebe seu próprio keyspace do Cassandra:
**Serviços com keyspaces por usuário:**
1. **Triples Query Service** (`trustgraph-flow/trustgraph/query/triples/cassandra/service.py:65`)
Usa `keyspace=query.user`
2. **Objects Query Service** (`trustgraph-flow/trustgraph/query/objects/cassandra/service.py:479`)
Usa `keyspace=self.sanitize_name(user)`
3. **KnowledgeGraph Direct Access** (`trustgraph-flow/trustgraph/direct/cassandra_kg.py:18`)
Parâmetro padrão `keyspace="trustgraph"`
**Status:** Estes **não são modificados** nesta especificação.
**Revisão Futura Necessária:**
Avaliar se o modelo de keyspace por usuário cria problemas de isolamento de locatários
Considerar se as implementações multi-locatário precisam de padrões de prefixo de keyspace (por exemplo, `tenant_a_user1`)
Revisar para possíveis colisões de ID de usuário entre locatários
Avaliar se um keyspace compartilhado único por locatário com isolamento de linha baseado em usuário é preferível
**Observação:** Isso não impede a implementação multi-locatário atual, mas deve ser revisado antes das implementações multi-locatário de produção.
## Fases de Implementação
### Fase 1: Correções de Parâmetros (Alterações 1-6)
Corrigir a nomenclatura do parâmetro `--config-push-queue`
Adicionar suporte ao parâmetro `--cassandra-keyspace`
**Resultado:** Configuração de fila e keyspace multi-locatário habilitada
### Fase 2: Migração do Gerenciamento de Coleções (Alterações 7-9, 11)
Migrar o armazenamento de coleções para o serviço de configuração
Remover a tabela de coleções do librarian
Atualizar o esquema de coleção (remover timestamps)
**Resultado:** Elimina tópicos de gerenciamento de armazenamento codificados, simplifica o librarian
### Fase 3: Atualizações do Serviço de Armazenamento (Alteração 10) ✅ CONCLUÍDA
Atualizados todos os serviços de armazenamento para usar o push de configuração para coleções via `CollectionConfigHandler`
Removida a infraestrutura de solicitação/resposta de gerenciamento de armazenamento
Removidas as definições de esquema legadas
**Resultado:** Gerenciamento de coleções baseado em configuração completo alcançado
## Referências
GitHub Issue: https://github.com/trustgraph-ai/trustgraph/issues/582
Arquivos Relacionados:
`trustgraph-base/trustgraph/base/async_processor.py`
`trustgraph-base/trustgraph/base/cassandra_config.py`
`trustgraph-base/trustgraph/schema/core/topic.py`
`trustgraph-base/trustgraph/schema/services/collection.py`
`trustgraph-flow/trustgraph/config/service/service.py`
`trustgraph-flow/trustgraph/cores/service.py`
`trustgraph-flow/trustgraph/librarian/service.py`
`trustgraph-flow/trustgraph/librarian/collection_manager.py`
`trustgraph-flow/trustgraph/tables/library.py`

View file

@ -0,0 +1,367 @@
---
layout: default
title: "Suporte para Isolamento de Usuário/Coleção no Neo4j"
parent: "Portuguese (Beta)"
---
# Suporte para Isolamento de Usuário/Coleção no 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.
## Declaração do Problema
A implementação atual de armazenamento e consulta de triplas do Neo4j carece de isolamento de usuário/coleção, o que cria um problema de segurança de multi-inquilinato. Todas as triplas são armazenadas no mesmo espaço de grafo sem nenhum mecanismo para impedir que os usuários acessem os dados de outros usuários ou misturem coleções.
Ao contrário de outros backends de armazenamento no TrustGraph:
**Cassandra**: Usa key spaces separados por usuário e tabelas por coleção.
**Armazenamentos vetoriais** (Milvus, Qdrant, Pinecone): Usam namespaces específicos para cada coleção.
**Neo4j**: Atualmente compartilha todos os dados em um único grafo (vulnerabilidade de segurança).
## Arquitetura Atual
### Modelo de Dados
**Nós**: Rótulo `:Node` com propriedade `uri`, rótulo `:Literal` com propriedade `value`.
**Relacionamentos**: Rótulo `:Rel` com propriedade `uri`.
**Índices**: `Node.uri`, `Literal.value`, `Rel.uri`.
### Fluxo de Mensagens
Mensagens `Triples` contêm campos `metadata.user` e `metadata.collection`.
O serviço de armazenamento recebe informações de usuário/coleção, mas as ignora.
O serviço de consulta espera `user` e `collection` em `TriplesQueryRequest`, mas os ignora.
### Problema de Segurança Atual
```cypher
# Any user can query any data - no isolation
MATCH (src:Node)-[rel:Rel]->(dest:Node)
RETURN src.uri, rel.uri, dest.uri
```
## Solução Proposta: Filtragem Baseada em Propriedades (Recomendada)
### Visão Geral
Adicione as propriedades `user` e `collection` a todos os nós e relacionamentos, e então filtre todas as operações por essas propriedades. Essa abordagem fornece um forte isolamento, mantendo a flexibilidade da consulta e a compatibilidade com versões anteriores.
### Alterações no Modelo de Dados
#### Estrutura de Nó Aprimorada
```cypher
// Node entities
CREATE (n:Node {
uri: "http://example.com/entity1",
user: "john_doe",
collection: "production_v1"
})
// Literal entities
CREATE (n:Literal {
value: "literal value",
user: "john_doe",
collection: "production_v1"
})
```
#### Estrutura de Relacionamento Aprimorada
```cypher
// Relationships with user/collection properties
CREATE (src)-[:Rel {
uri: "http://example.com/predicate1",
user: "john_doe",
collection: "production_v1"
}]->(dest)
```
#### Índices Atualizados
```cypher
// Compound indexes for efficient filtering
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);
// Maintain existing indexes for backwards compatibility (optional)
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);
```
### Alterações na Implementação
#### Serviço de Armazenamento (`write.py`)
**Código Atual:**
```python
def create_node(self, uri):
summary = self.io.execute_query(
"MERGE (n:Node {uri: $uri})",
uri=uri, database_=self.db,
).summary
```
**Código Atualizado:**
```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
```
**Método `store_triples` aprimorado:**
```python
async def store_triples(self, message):
user = message.metadata.user
collection = message.metadata.collection
for t in message.triples:
self.create_node(t.s.value, user, collection)
if t.o.is_uri:
self.create_node(t.o.value, user, collection)
self.relate_node(t.s.value, t.p.value, t.o.value, user, collection)
else:
self.create_literal(t.o.value, user, collection)
self.relate_literal(t.s.value, t.p.value, t.o.value, user, collection)
```
#### Serviço de Consulta (`service.py`)
**Código Atual:**
```python
records, summary, keys = self.io.execute_query(
"MATCH (src:Node {uri: $src})-[rel:Rel {uri: $rel}]->(dest:Node) "
"RETURN dest.uri as dest",
src=query.s.value, rel=query.p.value, database_=self.db,
)
```
**Código Atualizado:**
```python
records, summary, keys = self.io.execute_query(
"MATCH (src:Node {uri: $src, user: $user, collection: $collection})-"
"[rel:Rel {uri: $rel, user: $user, collection: $collection}]->"
"(dest:Node {user: $user, collection: $collection}) "
"RETURN dest.uri as dest",
src=query.s.value, rel=query.p.value,
user=query.user, collection=query.collection,
database_=self.db,
)
```
### Estratégia de Migração
#### Fase 1: Adicionar Propriedades a Novos Dados
1. Atualizar o serviço de armazenamento para adicionar propriedades de usuário/coleção a novas triplas.
2. Manter a compatibilidade com versões anteriores, não exigindo propriedades nas consultas.
3. Os dados existentes permanecem acessíveis, mas não isolados.
#### Fase 2: Migrar Dados Existentes
```cypher
// Migrate existing nodes (requires default user/collection assignment)
MATCH (n:Node) WHERE n.user IS NULL
SET n.user = 'legacy_user', n.collection = 'default_collection';
MATCH (n:Literal) WHERE n.user IS NULL
SET n.user = 'legacy_user', n.collection = 'default_collection';
MATCH ()-[r:Rel]->() WHERE r.user IS NULL
SET r.user = 'legacy_user', r.collection = 'default_collection';
```
#### Fase 3: Impor Isolamento
1. Atualizar o serviço de consulta para exigir filtragem por usuário/coleção.
2. Adicionar validação para rejeitar consultas sem o contexto adequado de usuário/coleção.
3. Remover caminhos de acesso a dados legados.
### Considerações de Segurança
#### Validação de Consulta
```python
async def query_triples(self, query):
# Validate user/collection parameters
if not query.user or not query.collection:
raise ValueError("User and collection must be specified")
# All queries must include user/collection filters
# ... rest of implementation
```
#### Prevenção de Injeção de Parâmetros
Use exclusivamente consultas parametrizadas
Valide os valores do usuário/coleção em relação a padrões permitidos
Considere a sanitização para os requisitos de nomes de propriedades do Neo4j
#### Rastreamento de Auditoria
```python
logger.info(f"Query executed - User: {query.user}, Collection: {query.collection}, "
f"Pattern: {query.s}/{query.p}/{query.o}")
```
## Abordagens Alternativas Consideradas
### Opção 2: Isolamento Baseado em Rótulos
**Abordagem**: Utilize rótulos dinâmicos como `User_john_Collection_prod`
**Vantagens:**
Forte isolamento através da filtragem de rótulos
Desempenho de consulta eficiente com índices de rótulos
Separação clara de dados
**Desvantagens:**
O Neo4j possui limites práticos no número de rótulos (aproximadamente 1000)
Geração e sanitização complexas de nomes de rótulos
Difícil consultar entre coleções quando necessário
**Exemplo de Implementação:**
```cypher
CREATE (n:Node:User_john_Collection_prod {uri: "http://example.com/entity"})
MATCH (n:User_john_Collection_prod) WHERE n:Node RETURN n
```
### Opção 3: Banco de Dados por Usuário
**Abordagem**: Criar bancos de dados Neo4j separados para cada usuário ou combinação de usuário/coleção.
**Vantagens:**
Isolamento completo de dados
Sem risco de contaminação cruzada
Escalonamento independente por usuário
**Desvantagens:**
Sobrecarga de recursos (cada banco de dados consome memória)
Gerenciamento complexo do ciclo de vida do banco de dados
Limites do banco de dados da Edição Community do Neo4j
Análise entre usuários difícil
### Opção 4: Estratégia de Chave Composta
**Abordagem**: Prefixar todos os URIs e valores com informações do usuário/coleção.
**Vantagens:**
Compatível com consultas existentes
Implementação simples
Não requer alterações no esquema
**Desvantagens:**
A poluição de URIs afeta a semântica dos dados
Consultas menos eficientes (correspondência de prefixo de string)
Viola padrões RDF/web semântico
**Exemplo de Implementação:**
```python
def make_composite_uri(uri, user, collection):
return f"usr:{user}:col:{collection}:uri:{uri}"
```
## Plano de Implementação
### Fase 1: Fundação (Semana 1)
1. [ ] Atualizar o serviço de armazenamento para aceitar e armazenar propriedades de usuário/coleção
2. [ ] Adicionar índices compostos para consultas eficientes
3. [ ] Implementar uma camada de compatibilidade retroativa
4. [ ] Criar testes unitários para novas funcionalidades
### Fase 2: Atualizações de Consulta (Semana 2)
1. [ ] Atualizar todos os padrões de consulta para incluir filtros de usuário/coleção
2. [ ] Adicionar validação de consulta e verificações de segurança
3. [ ] Atualizar testes de integração
4. [ ] Testes de desempenho com consultas filtradas
### Fase 3: Migração e Implantação (Semana 3)
1. [ ] Criar scripts de migração de dados para instâncias Neo4j existentes
2. [ ] Documentação de implantação e manuais de operação
3. [ ] Monitoramento e alertas para violações de isolamento
4. [ ] Testes de ponta a ponta com vários usuários/coleções
### Fase 4: Reforço (Semana 4)
1. [ ] Remover o modo de compatibilidade legado
2. [ ] Adicionar registro de auditoria abrangente
3. [ ] Revisão de segurança e testes de penetração
4. [ ] Otimização de desempenho
## Estratégia de Testes
### Testes Unitários
```python
def test_user_collection_isolation():
# Store triples for user1/collection1
processor.store_triples(triples_user1_coll1)
# Store triples for user2/collection2
processor.store_triples(triples_user2_coll2)
# Query as user1 should only return user1's data
results = processor.query_triples(query_user1_coll1)
assert all_results_belong_to_user1_coll1(results)
# Query as user2 should only return user2's data
results = processor.query_triples(query_user2_coll2)
assert all_results_belong_to_user2_coll2(results)
```
### Testes de Integração
Cenários multiusuário com dados sobrepostos
Consultas entre coleções (devem falhar)
Testes de migração com dados existentes
Testes de desempenho com grandes conjuntos de dados
### Testes de Segurança
Tentativa de consultar dados de outros usuários
Ataques de injeção de SQL em parâmetros de usuário/coleção
Verificar o isolamento completo sob vários padrões de consulta
## Considerações de Desempenho
### Estratégia de Indexação
Índices compostos em `(user, collection, uri)` para filtragem otimizada
Considere índices parciais se algumas coleções forem muito maiores
Monitore o uso de índices e o desempenho das consultas
### Otimização de Consultas
Use EXPLAIN para verificar o uso de índices em consultas filtradas
Considere o cache de resultados de consulta para dados acessados com frequência
Monitore o uso de memória com um grande número de usuários/coleções
### Escalabilidade
Cada combinação de usuário/coleção cria ilhas de dados separadas
Monitore o tamanho do banco de dados e o uso do pool de conexões
Considere estratégias de escalabilidade horizontal, se necessário
## Segurança e Conformidade
### Garantias de Isolamento de Dados
**Físico**: Todos os dados do usuário armazenados com propriedades explícitas de usuário/coleção
**Lógico**: Todas as consultas filtradas pelo contexto de usuário/coleção
**Controle de Acesso**: A validação no nível do serviço impede o acesso não autorizado
### Requisitos de Auditoria
Registre todo o acesso a dados com o contexto de usuário/coleção
Rastreie atividades de migração e movimentação de dados
Monitore tentativas de violação de isolamento
### Considerações de Conformidade
GDPR: Capacidade aprimorada de localizar e excluir dados específicos do usuário
SOC2: Isolamento claro de dados e controles de acesso
HIPAA: Forte isolamento de locatários para dados de saúde
## Riscos e Mitigações
| Risco | Impacto | Probabilidade | Mitigação |
|------|--------|------------|------------|
| Consulta sem o filtro de usuário/coleção | Alto | Médio | Validação obrigatória, testes abrangentes |
| Degradação de desempenho | Médio | Baixo | Otimização de índice, análise de desempenho |
| Corrupção de dados durante a migração | Alto | Baixo | Estratégia de backup, procedimentos de reversão |
| Consultas complexas entre várias coleções | Médio | Médio | Documente padrões de consulta, forneça exemplos |
## Critérios de Sucesso
1. **Segurança**: Zero acesso a dados de outros usuários em produção
2. **Desempenho**: Impacto de <10% no desempenho da consulta em comparação com consultas não filtradas
3. **Migração**: 100% dos dados existentes migrados com sucesso e sem perda
4. **Usabilidade**: Todos os padrões de consulta existentes funcionam com o contexto de usuário/coleção
5. **Conformidade**: Rastreamento completo de acesso a dados de usuário/coleção
## Conclusão
A abordagem de filtragem baseada em propriedades oferece o melhor equilíbrio entre segurança, desempenho e manutenção para adicionar o isolamento de usuário/coleção ao Neo4j. Ela se alinha aos padrões de multilocação existentes do TrustGraph, aproveitando os pontos fortes do Neo4j em consultas de grafos e indexação.
Esta solução garante que o backend do Neo4j do TrustGraph atenda aos mesmos padrões de segurança de outros backends de armazenamento, prevenindo vulnerabilidades de isolamento de dados, mantendo a flexibilidade e o poder das consultas de grafos.

View file

@ -0,0 +1,769 @@
---
layout: default
title: "Extração de Conhecimento de Ontologias - Fase 2 de Refatoração"
parent: "Portuguese (Beta)"
---
# Extração de Conhecimento de Ontologias - Fase 2 de Refatoração
> **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.
**Status**: Rascunho
**Autor**: Sessão de Análise 2025-12-03
**Relacionado**: `ontology.md`, `ontorag.md`
## Visão Geral
Este documento identifica inconsistências no sistema atual de extração de conhecimento baseado em ontologias e propõe uma refatoração para melhorar o desempenho do LLM e reduzir a perda de informações.
## Implementação Atual
### Como Funciona Atualmente
1. **Carregamento da Ontologia** (`ontology_loader.py`)
Carrega o arquivo JSON da ontologia com chaves como `"fo/Recipe"`, `"fo/Food"`, `"fo/produces"`
Os IDs das classes incluem o prefixo do namespace na própria chave
Exemplo de `food.ontology`:
```json
"classes": {
"fo/Recipe": {
"uri": "http://purl.org/ontology/fo/Recipe",
"rdfs:comment": "A Recipe is a combination..."
}
}
```
2. **Construção do Prompt** (`extract.py:299-307`, `ontology-prompt.md`)
O modelo recebe os dicionários `classes`, `object_properties`, `datatype_properties`
O modelo itera: `{% for class_id, class_def in classes.items() %}`
O LLM vê: `**fo/Recipe**: A Recipe is a combination...`
O formato de saída de exemplo mostra:
```json
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}
{"subject": "recipe:cornish-pasty", "predicate": "has_ingredient", "object": "ingredient:flour"}
```
3. **Análise da Resposta** (`extract.py:382-428`)
Espera um array JSON: `[{"subject": "...", "predicate": "...", "object": "..."}]`
Valida em relação a um subconjunto da ontologia
Expande URIs via `expand_uri()` (extract.py:473-521)
4. **Expansão de URIs** (`extract.py:473-521`)
Verifica se o valor está no dicionário `ontology_subset.classes`
Se encontrado, extrai o URI da definição da classe
Se não encontrado, constrói o URI: `f"https://trustgraph.ai/ontology/{ontology_id}#{value}"`
### Exemplo de Fluxo de Dados
**JSON da Ontologia → Loader → Prompt:**
```
"fo/Recipe" → classes["fo/Recipe"] → LLM sees "**fo/Recipe**"
```
**LLM → Parser → Output:**
```
"Recipe" → not in classes["fo/Recipe"] → constructs URI → LOSES original URI
"fo/Recipe" → found in classes → uses original URI → PRESERVES URI
```
## Problemas Identificados
### 1. **Exemplos Inconsistentes no Prompt**
**Problema**: O modelo de prompt mostra IDs de classe com prefixos (`fo/Recipe`), mas a saída de exemplo usa nomes de classe sem prefixo (`Recipe`).
**Localização**: `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**: O LLM recebe sinais conflitantes sobre qual formato usar.
### 2. **Perda de Informação na Expansão de URIs**
**Problema**: Quando o LLM retorna nomes de classe sem prefixo, seguindo o exemplo, `expand_uri()` não consegue encontrá-los no dicionário de ontologias e constrói URIs de fallback, perdendo os URIs originais corretos.
**Localização**: `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 construído: `https://trustgraph.ai/ontology/food#Recipe`
Perda de significado semântico, quebra a interoperabilidade
### 3. **Formato Ambíguo de Instância de Entidade**
**Problema:** Não há orientação clara sobre o formato do URI da instância de entidade.
**Exemplos no prompt:**
`"recipe:cornish-pasty"` (prefixo semelhante a um namespace)
`"ingredient:flour"` (prefixo diferente)
**Comportamento 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**: O modelo de linguagem deve adivinhar a convenção de prefixo sem contexto ontológico.
### 4. **Sem Orientação de Prefixo de Namespace**
**Problema**: O JSON da ontologia contém definições de namespace (linha 10-25 em food.ontology):
```json
"namespaces": {
"fo": "http://purl.org/ontology/fo/",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
...
}
```
Mas estas informações nunca são transmitidas para o LLM. O LLM não sabe:
O que "fo" significa
Qual prefixo usar para entidades
Qual namespace se aplica a quais elementos
### 5. **Rótulos Não Utilizados no Prompt**
**Problema**: Cada classe tem campos `rdfs:label` (por exemplo, `{"value": "Recipe", "lang": "en-gb"}`), mas o modelo de prompt não os utiliza.
**Atual**: Mostra apenas `class_id` e `comment`
```jinja
- **{{class_id}}**{% if class_def.comment %}: {{class_def.comment}}{% endif %}
```
**Disponível, mas não utilizado**:
```python
"rdfs:label": [{"value": "Recipe", "lang": "en-gb"}]
```
**Impacto**: Poderia fornecer nomes legíveis para humanos, juntamente com IDs técnicos.
## Soluções Propostas
### Opção A: Normalizar para IDs sem Prefixo
**Abordagem**: Remover os prefixos dos IDs de classe antes de exibi-los para o LLM.
**Alterações**:
1. Modificar `build_extraction_variables()` para transformar chaves:
```python
classes_for_prompt = {
k.split('/')[-1]: v # "fo/Recipe" → "Recipe"
for k, v in ontology_subset.classes.items()
}
```
2. Atualizar o exemplo de prompt para corresponder (já usa nomes sem prefixo).
3. Modificar `expand_uri()` para lidar com ambos os 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']
```
**Prós:**
Mais limpo, mais legível para humanos
Compatível com exemplos de prompts existentes
Modelos de linguagem grandes (LLMs) funcionam melhor com tokens mais simples
**Contras:**
Colisões de nomes de classe se múltiplas ontologias tiverem o mesmo nome de classe
Perde informações de namespace
Requer lógica de fallback para pesquisas
### Opção B: Usar IDs com Prefixo Completo Consistentemente
**Abordagem:** Atualizar exemplos para usar IDs com prefixo correspondentes ao que é mostrado na lista de classes.
**Mudanças:**
1. Atualizar exemplo de 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. Adicionar explicação do namespace ao prompt:
```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. Mantenha `expand_uri()` como está (funciona corretamente quando as correspondências são encontradas).
**Prós:**
Consistência entre entrada e saída.
Sem perda de informação.
Preserva a semântica do namespace.
Funciona com múltiplas ontologias.
**Contras:**
Tokens mais verbosos para o LLM.
Requer que o LLM rastreie os prefixos.
### Opção C: Híbrida - Mostrar Tanto o Rótulo quanto o ID
**Abordagem:** Aprimorar o prompt para mostrar tanto os rótulos legíveis por humanos quanto os IDs técnicos.
**Alterações:**
1. Atualizar o modelo do 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 %}
```
Exemplo de saída:
```markdown
- **fo/Recipe** (label: "Recipe"): A Recipe is a combination...
```
2. Instruções de atualização:
```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
```
**Prós**:
Mais claro para LLM
Preserva todas as informações
Explícito sobre o que usar
**Contras**:
Prompt mais longo
Modelo mais complexo
## Abordagem Implementada
**Formato Simplificado de Entidade-Relacionamento-Atributo** - substitui completamente o formato antigo baseado em triplas.
A nova abordagem foi escolhida porque:
1. **Nenhuma Perda de Informação**: URIs originais preservados corretamente
2. **Lógica Mais Simples**: Nenhuma transformação necessária, pesquisas diretas em dicionários funcionam
3. **Segurança de Namespace**: Lida com múltiplas ontologias sem colisões
4. **Correção Semântica**: Mantém a semântica RDF/OWL
## Implementação Completa
### O Que Foi Construído:
1. **Novo Modelo de Prompt** (`prompts/ontology-extract-v2.txt`)
✅ Seções claras: Tipos de Entidade, Relacionamentos, Atributos
✅ Exemplo usando identificadores de tipo completos (`fo/Recipe`, `fo/has_ingredient`)
✅ Instruções para usar identificadores exatos do esquema
✅ Novo formato JSON com arrays de entidades/relacionamentos/atributos
2. **Normalização de Entidade** (`entity_normalizer.py`)
`normalize_entity_name()` - Converte nomes para formato seguro para URI
`normalize_type_identifier()` - Lida com barras em tipos (`fo/Recipe``fo-recipe`)
`build_entity_uri()` - Cria URIs únicos usando a tupla (nome, tipo)
`EntityRegistry` - Rastreia entidades para desduplicação
3. **Analisador JSON** (`simplified_parser.py`)
✅ Analisa o novo formato: `{entities: [...], relationships: [...], attributes: [...]}`
✅ Suporta nomes de campos em kebab-case e snake_case
✅ Retorna dataclasses estruturadas
✅ Tratamento de erros elegante com registro
4. **Conversor de Triplas** (`triple_converter.py`)
`convert_entity()` - Gera automaticamente triplas de tipo + rótulo
`convert_relationship()` - Conecta URIs de entidade via propriedades
`convert_attribute()` - Adiciona valores literais
✅ Consulta URIs completos a partir de definições de ontologia
5. **Processador Principal Atualizado** (`extract.py`)
✅ Removeu o código antigo de extração baseado em triplas
✅ Adicionado método `extract_with_simplified_format()`
✅ Agora usa exclusivamente o novo formato simplificado
✅ Chama o prompt com o ID `extract-with-ontologies-v2`
## Casos de Teste
### Teste 1: Preservação 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"
```
### Teste 2: Colisão Multi-Ontologia
```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"
```
### Teste 3: Formato de Instância de Entidade
```python
# Given prompt with food ontology
# LLM should create instances like
{"subject": "recipe:cornish-pasty"} # Namespace-style
{"subject": "food:beef"} # Consistent prefix
```
## Perguntas Abertas
1. **As instâncias de entidades devem usar prefixos de namespace?**
Atual: `"recipe:cornish-pasty"` (arbitrário)
Alternativa: Usar o prefixo da ontologia `"fo:cornish-pasty"`?
Alternativa: Sem prefixo, expandir no URI `"cornish-pasty"` → URI completo?
2. **Como lidar com o domínio/alcance no prompt?**
Atualmente mostra: `(Recipe → Food)`
Deveria ser: `(fo/Recipe → fo/Food)`?
3. **Devemos validar as restrições de domínio/alcance?**
TODO: comentário em extract.py:470
Detectaria mais erros, mas seria mais complexo
4. **O que dizer das propriedades inversas e equivalências?**
A ontologia tem `owl:inverseOf`, `owl:equivalentClass`
Não está atualmente sendo usado na extração
Deveria estar?
## Métricas de Sucesso
✅ Perda zero de informações de URI (100% de preservação dos URIs originais)
✅ O formato de saída do LLM corresponde ao formato de entrada
✅ Nenhum exemplo ambíguo no prompt
✅ Os testes passam com múltiplas ontologias
✅ Qualidade de extração aprimorada (medida pela porcentagem de triplas válidas)
## Abordagem Alternativa: Formato de Extração Simplificado
### Filosofia
Em vez de pedir ao LLM para entender a semântica RDF/OWL, peça para ele fazer o que ele faz de melhor: **encontrar entidades e relacionamentos no texto**.
Deixe que o código lide com a construção de URIs, a conversão RDF e as formalidades da web semântica.
### Exemplo: Classificação de Entidades
**Texto de Entrada:**
```
Cornish pasty is a traditional British pastry filled with meat and vegetables.
```
**Esquema de Ontologia (mostrado ao 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
```
**O que o LLM retorna (JSON simples):**
```json
{
"entities": [
{
"entity": "Cornish pasty",
"type": "Recipe"
}
]
}
```
**O que o Código Produz (Triplas 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)
)
]
```
### Benefícios
1. **O LLM não precisa de:**
Entender a sintaxe de URI
Inventar prefixos de identificadores (`recipe:`, `ingredient:`)
Saber sobre `rdf:type` ou `rdfs:label`
Construir identificadores da web semântica
2. **O LLM precisa apenas de:**
Encontrar entidades no texto
Mapeá-las para classes de ontologia
Extrair relacionamentos e atributos
3. **O código lida com:**
Normalização e construção de URI
Geração de triplas RDF
Atribuição automática de rótulos
Gerenciamento de namespaces
### Por que Isso Funciona Melhor
**Prompt mais simples** = menos confusão = menos erros
**IDs consistentes** = o código controla as regras de normalização
**Rótulos gerados automaticamente** = sem triplas rdfs:label ausentes
**O LLM se concentra na extração** = o que ele realmente faz bem
### Exemplo: Relacionamentos de Entidades
**Texto de entrada:**
```
Cornish pasty is a traditional British pastry filled with beef and potatoes.
```
**Esquema de Ontologia (mostrado ao 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)
```
**O que o LLM retorna (JSON simples):**
```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"
}
]
}
```
**O que o Código Produz (Triplas 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)
)
]
```
**Pontos-chave:**
O LLM retorna nomes de entidades em linguagem natural: `"Cornish pasty"`, `"beef"`, `"potatoes"`
O LLM inclui tipos para disambiguar: `subject-type`, `object-type`
O LLM usa o nome da relação do esquema: `"has_ingredient"`
O código deriva IDs consistentes usando (nome, tipo): `("Cornish pasty", "Recipe")``recipe-cornish-pasty`
O código pesquisa o URI da relação na ontologia: `fo/has_ingredient` → URI completo
A mesma tupla (nome, tipo) sempre obtém o mesmo URI (desduplicação)
### Exemplo: Desambiguação de Nomes de Entidades
**Problema:** O mesmo nome pode se referir a diferentes tipos de entidade.
**Caso real:**
```
"Cornish pasty" can be:
- A Recipe (instructions for making it)
- A Food (the dish itself)
```
**Como é tratado:**
O LLM retorna 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"
}
]
}
```
**Resolução 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 que isso funciona:**
O tipo está incluído em TODAS as referências (entidades, relacionamentos, atributos).
O código usa a tupla `(name, type)` como chave de pesquisa.
Sem ambiguidades, sem colisões.
### Exemplo: Atributos de Entidade
**Texto de entrada:**
```
This Cornish pasty recipe serves 4-6 people and takes 45 minutes to prepare.
```
**Esquema de Ontologia (mostrado ao 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)
```
**O que o LLM retorna (JSON simples):**
```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"
}
]
}
```
**O que o Código Produz (Triplas 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!
)
]
```
**Pontos-chave:**
O LLM extrai valores literais: `"4-6 people"`, `"45 minutes"`
O LLM inclui o tipo de entidade para desambiguação: `entity-type`
O LLM usa o nome do atributo do esquema: `"serves"`, `"preparation_time"`
O código pesquisa o URI do atributo a partir das propriedades do tipo de dados da ontologia
**O objeto é literal** (`is_uri=False`), não uma referência de URI
Os valores permanecem como texto natural, sem necessidade de normalização
**Diferença em relação a Relacionamentos:**
Relacionamentos: tanto o sujeito quanto o objeto são entidades (URIs)
Atributos: o sujeito é uma entidade (URI), o objeto é um valor literal (string/número)
### Exemplo Completo: Entidades + Relacionamentos + Atributos
**Texto de Entrada:**
```
Cornish pasty is a savory pastry filled with beef and potatoes.
This recipe serves 4 people.
```
**O que o LLM retorna:**
```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:** 11 triplas RDF geradas:
3 triplas de tipo de entidade (rdf:type)
3 triplas de rótulo de entidade (rdfs:label) - automático
2 triplas de relacionamento (has_ingredient)
1 tripla de atributo (serves)
Tudo isso a partir de extrações simples e em linguagem natural pelo LLM!
## Referências
Implementação atual: `trustgraph-flow/trustgraph/extract/kg/ontology/extract.py`
Modelo de prompt: `ontology-prompt.md`
Casos de teste: `tests/unit/test_extract/test_ontology/`
Ontologia de exemplo: `e2e/test-data/food.ontology`

View file

@ -0,0 +1,294 @@
---
layout: default
title: "Especificação Técnica da Estrutura da Ontologia"
parent: "Portuguese (Beta)"
---
# Especificação Técnica da Estrutura da Ontologia
> **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.
## Visão Geral
Esta especificação descreve a estrutura e o formato das ontologias dentro do sistema TrustGraph. As ontologias fornecem modelos de conhecimento formais que definem classes, propriedades e relacionamentos, suportando capacidades de raciocínio e inferência. O sistema usa um formato de configuração inspirado em OWL que representa amplamente os conceitos OWL/RDFS, ao mesmo tempo que é otimizado para os requisitos do TrustGraph.
**Convenção de Nomenclatura**: Este projeto usa kebab-case para todos os identificadores (chaves de configuração, pontos finais de API, nomes de módulos, etc.) em vez de snake_case.
## Objetivos
**Gerenciamento de Classes e Propriedades**: Definir classes semelhantes a OWL com propriedades, domínios, intervalos e restrições de tipo.
**Suporte Semântico Rico**: Habilitar propriedades abrangentes RDFS/OWL, incluindo rótulos, suporte multilíngue e restrições formais.
**Suporte a Múltiplas Ontologias**: Permitir que várias ontologias coexistam e interoperem.
**Validação e Raciocínio**: Garantir que as ontologias estejam em conformidade com padrões semelhantes a OWL, com verificação de consistência e suporte de inferência.
**Compatibilidade com Padrões**: Suportar importação/exportação em formatos padrão (Turtle, RDF/XML, OWL/XML) mantendo a otimização interna.
## Contexto
O TrustGraph armazena ontologias como itens de configuração em um sistema flexível de chave-valor. Embora o formato seja inspirado em OWL (Web Ontology Language), ele é otimizado para casos de uso específicos do TrustGraph e não adere estritamente a todas as especificações do OWL.
As ontologias no TrustGraph permitem:
Definição de tipos de objetos formais e suas propriedades.
Especificação de domínios, intervalos e restrições de tipo de propriedade.
Raciocínio e inferência lógica.
Relacionamentos complexos e restrições de cardinalidade.
Suporte multilíngue para internacionalização.
## Estrutura da Ontologia
### Armazenamento de Configuração
As ontologias são armazenadas como itens de configuração com o seguinte padrão:
**Tipo**: `ontology`
**Chave**: Identificador de ontologia exclusivo (por exemplo, `natural-world`, `domain-model`)
**Valor**: Ontologia completa em formato JSON.
### Estrutura JSON
O formato JSON da ontologia consiste em quatro seções principais:
#### 1. Metadados
Contém informações administrativas e descritivas sobre a ontologia:
```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`: Nome legível para humanos da ontologia
`description`: Breve descrição do propósito da ontologia
`version`: Número de versão semântico
`created`: Carimbo de data/hora ISO 8601 de criação
`modified`: Carimbo de data/hora ISO 8601 da última modificação
`creator`: Identificador do usuário/sistema que criou
`namespace`: URI base para elementos da ontologia
`imports`: Array de URIs de ontologias importadas
#### 2. Classes
Define os tipos de objeto e seus relacionamentos hierárquicos:
```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"
}
}
}
```
**Propriedades Suportadas:**
`uri`: URI completo da classe
`type`: Sempre `"owl:Class"`
`rdfs:label`: Array de rótulos com informações de idioma
`rdfs:comment`: Descrição da classe
`rdfs:subClassOf`: Identificador da classe pai (herança simples)
`owl:equivalentClass`: Array de identificadores de classes equivalentes
`owl:disjointWith`: Array de identificadores de classes disjuntas
`dcterms:identifier`: Identificador de referência externa opcional
#### 3. Propriedades de Objeto
Propriedades que conectam instâncias a outras instâncias:
```json
{
"objectProperties": {
"has-parent": {
"uri": "http://trustgraph.ai/ontologies/natural-world#has-parent",
"type": "owl:ObjectProperty",
"rdfs:label": [{"value": "has parent", "lang": "en"}],
"rdfs:comment": "Links an animal to its parent",
"rdfs:domain": "animal",
"rdfs:range": "animal",
"owl:inverseOf": "parent-of",
"owl:functionalProperty": false
}
}
}
```
**Propriedades Suportadas:**
`uri`: URI completo da propriedade
`type`: Sempre `"owl:ObjectProperty"`
`rdfs:label`: Array de rótulos com informações de idioma
`rdfs:comment`: Descrição da propriedade
`rdfs:domain`: Identificador da classe que possui esta propriedade
`rdfs:range`: Identificador da classe para valores de propriedade
`owl:inverseOf`: Identificador da propriedade inversa
`owl:functionalProperty`: Booleano indicando no máximo um valor
`owl:inverseFunctionalProperty`: Booleano para propriedades de identificação única
#### 4. Propriedades de Tipo de Dado
Propriedades que conectam instâncias a valores literais:
```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:domain": "animal",
"rdfs:range": "xsd:nonNegativeInteger",
"owl:functionalProperty": true,
"owl:minCardinality": 0,
"owl:maxCardinality": 1
}
}
}
```
**Propriedades Suportadas:**
`uri`: URI completo da propriedade
`type`: Sempre `"owl:DatatypeProperty"`
`rdfs:label`: Array de rótulos com marcação de idioma
`rdfs:comment`: Descrição da propriedade
`rdfs:domain`: Identificador da classe que possui esta propriedade
`rdfs:range`: Tipo de dados XSD para os valores da propriedade
`owl:functionalProperty`: Booleano indicando no máximo um valor
`owl:minCardinality`: Número mínimo de valores (opcional)
`owl:maxCardinality`: Número máximo de valores (opcional)
`owl:cardinality`: Número exato de valores (opcional)
### Tipos de Dados XSD Suportados
Os seguintes tipos de dados XML Schema são suportados para intervalos de propriedades:
`xsd:string` - Valores de texto
`xsd:integer` - Números inteiros
`xsd:nonNegativeInteger` - Inteiros não negativos
`xsd:float` - Números de ponto flutuante
`xsd:double` - Números de precisão dupla
`xsd:boolean` - Valores verdadeiro/falso
`xsd:dateTime` - Valores de data e hora
`xsd:date` - Valores de data
`xsd:anyURI` - Referências de URI
### Suporte a Idiomas
Rótulos e comentários suportam vários idiomas usando o formato de etiqueta de idioma W3C:
```json
{
"rdfs:label": [
{"value": "Animal", "lang": "en"},
{"value": "Tier", "lang": "de"},
{"value": "Animal", "lang": "es"}
]
}
```
## Exemplo de Ontologia
Aqui está um exemplo completo de uma ontologia simples:
```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#"]
},
"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"
}
}
}
```
## Regras de Validação
### Validação Estrutural
1. **Consistência de URIs**: Todos os URIs devem seguir o padrão `{namespace}#{identifier}`
2. **Hierarquia de Classes**: Não pode haver herança circular em `rdfs:subClassOf`
3. **Domínios/Intervalos de Propriedades**: Deve referenciar classes existentes ou tipos XSD válidos
4. **Classes Disjuntas**: Não podem ser subclasses umas das outras
5. **Propriedades Inversas**: Devem ser bidirecionais se especificadas
### Validação Semântica
1. **Identificadores Únicos**: Os identificadores de classe e propriedade devem ser únicos dentro de uma ontologia
2. **Etiquetas de Idioma**: Devem seguir o formato de etiqueta de idioma BCP 47
3. **Restrições de Cardinalidade**: `minCardinality``maxCardinality` quando ambos são especificados
4. **Propriedades Funcionais**: Não podem ter `maxCardinality` > 1
## Suporte a Formatos de Importação/Exportação
Embora o formato interno seja JSON, o sistema suporta a conversão para/de formatos de ontologia padrão:
**Turtle (.ttl)** - Serialização RDF compacta
**RDF/XML (.rdf, .owl)** - Formato padrão W3C
**OWL/XML (.owx)** - Formato XML específico para OWL
**JSON-LD (.jsonld)** - JSON para Dados Vinculados
## Referências
[OWL 2 Web Ontology Language](https://www.w3.org/TR/owl2-overview/)
[RDF Schema 1.1](https://www.w3.org/TR/rdf-schema/)
[XML Schema Datatypes](https://www.w3.org/TR/xmlschema-2/)
[BCP 47 Language Tags](https://tools.ietf.org/html/bcp47)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,239 @@
---
layout: default
title: "Especificação OpenAPI - Especificação Técnica"
parent: "Portuguese (Beta)"
---
# Especificação OpenAPI - Especificação 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
Criar uma especificação OpenAPI 3.1 abrangente e modular para o Gateway de API REST TrustGraph que:
Documenta todos os endpoints REST
Utiliza `$ref` externo para modularidade e manutenção
Mapeia diretamente para o código do tradutor de mensagens
Fornece esquemas precisos de requisição/resposta
## Fonte da Verdade
A API é definida por:
**Tradutores de Mensagens**: `trustgraph-base/trustgraph/messaging/translators/*.py`
**Gerenciador de Dispatcher**: `trustgraph-flow/trustgraph/gateway/dispatch/manager.py`
**Gerenciador de Endpoint**: `trustgraph-flow/trustgraph/gateway/endpoint/manager.py`
## Estrutura de Diretórios
```
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
```
## Mapeamento de Serviços
### Serviços Globais (`/api/v1/{kind}`)
`config` - Gerenciamento de configuração
`flow` - Ciclo de vida do fluxo
`librarian` - Biblioteca de documentos
`knowledge` - Núcleos de conhecimento
`collection-management` - Metadados de coleção
### Serviços Hospedados em Fluxo (`/api/v1/flow/{flow}/service/{kind}`)
**Requisição/Resposta:**
`agent`, `text-completion`, `prompt`, `mcp-tool`
`graph-rag`, `document-rag`
`embeddings`, `graph-embeddings`, `document-embeddings`
`triples`, `objects`, `nlp-query`, `structured-query`, `structured-diag`
**Enviar e Esquecer:**
`text-load`, `document-load`
### Importação/Exportação
`/api/v1/import-core` (POST)
`/api/v1/export-core` (GET)
`/api/v1/flow/{flow}/import/{kind}` (WebSocket)
`/api/v1/flow/{flow}/export/{kind}` (WebSocket)
### Outros
`/api/v1/socket` (WebSocket multiplexado)
`/api/metrics` (Prometheus)
## Abordagem
### Fase 1: Configuração
1. Criar estrutura de diretórios
2. Criar o arquivo principal `openapi.yaml` com metadados, servidores, segurança
3. Criar componentes reutilizáveis (erros, parâmetros comuns, esquemas de segurança)
### Fase 2: Esquemas Comuns
Criar esquemas compartilhados usados em todos os serviços:
`RdfValue`, `Triple` - Estruturas RDF/triplas
`ErrorObject` - Resposta de erro
`DocumentMetadata`, `ProcessingMetadata` - Estruturas de metadados
Parâmetros comuns: `FlowId`, `User`, `Collection`
### Fase 3: Serviços Globais
Para cada serviço global (configuração, fluxo, bibliotecário, conhecimento, gerenciamento de coleção):
1. Criar arquivo de caminho em `paths/`
2. Criar esquema de solicitação em `components/schemas/{service}/`
3. Criar esquema de resposta
4. Adicionar exemplos
5. Referenciar do arquivo principal `openapi.yaml`
### Fase 4: Serviços Hospedados em Fluxo
Para cada serviço hospedado em fluxo:
1. Criar arquivo de caminho em `paths/flow-services/`
2. Criar esquemas de solicitação/resposta em `components/schemas/ai-services/`
3. Adicionar documentação da flag de streaming, quando aplicável
4. Referenciar do arquivo principal `openapi.yaml`
### Fase 5: Importação/Exportação e WebSocket
1. Documentar os principais endpoints de importação/exportação
2. Documentar os padrões de protocolo WebSocket
3. Documentar os endpoints de importação/exportação WebSocket no nível do fluxo
### Fase 6: Validação
1. Validar com ferramentas de validação OpenAPI
2. Testar com Swagger UI
3. Verificar se todos os tradutores estão cobertos
## Convenção de Nomenclatura de Campos
Todos os campos JSON usam **kebab-case**:
`flow-id`, `blueprint-name`, `doc-limit`, `entity-limit`, etc.
## Criação de Arquivos de Esquema
Para cada tradutor em `trustgraph-base/trustgraph/messaging/translators/`:
1. **Ler o método do tradutor `to_pulsar()`** - Define o esquema de solicitação
2. **Ler o método do tradutor `from_pulsar()`** - Define o esquema de resposta
3. **Extrair nomes e tipos de campos**
4. **Criar o esquema OpenAPI** com:
Nomes de campos (kebab-case)
Tipos (string, integer, boolean, object, array)
Campos obrigatórios
Valores padrão
Descrições
### Exemplo de Processo de Mapeamento
```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
)
```
Tradução para:
```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
```
## Respostas de Streaming
Serviços que suportam streaming retornam múltiplas respostas com a flag `end_of_stream`:
`agent`, `text-completion`, `prompt`
`document-rag`, `graph-rag`
Documente este padrão no esquema de resposta de cada serviço.
## Respostas de Erro
Todos os serviços podem retornar:
```yaml
error:
oneOf:
- type: string
- $ref: '#/components/schemas/ErrorObject'
```
Onde `ErrorObject` está:
```yaml
type: object
properties:
type:
type: string
message:
type: string
```
## Referências
Tradutores: `trustgraph-base/trustgraph/messaging/translators/`
Mapeamento do despachante: `trustgraph-flow/trustgraph/gateway/dispatch/manager.py`
Roteamento de endpoint: `trustgraph-flow/trustgraph/gateway/endpoint/manager.py`
Resumo do serviço: `API_SERVICES_SUMMARY.md`

View file

@ -0,0 +1,965 @@
---
layout: default
title: "Infraestrutura Pub/Sub"
parent: "Portuguese (Beta)"
---
# Infraestrutura 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.
## Visão Geral
Este documento cataloga todas as conexões entre o código-fonte do TrustGraph e a infraestrutura pub/sub. Atualmente, o sistema está codificado para usar o Apache Pulsar. Esta análise identifica todos os pontos de integração para informar futuras refatorações em direção a uma abstração pub/sub configurável.
## Estado Atual: Pontos de Integração do Pulsar
### 1. Uso Direto do Cliente Pulsar
**Localização:** `trustgraph-flow/trustgraph/gateway/service.py`
O gateway da API importa e instancia diretamente o cliente Pulsar:
**Linha 20:** `import pulsar`
**Linhas 54-61:** Instanciação direta de `pulsar.Client()` com `pulsar.AuthenticationToken()` opcional
**Linhas 33-35:** Configuração padrão do host Pulsar a partir de variáveis de ambiente
**Linhas 178-192:** Argumentos da linha de comando para `--pulsar-host`, `--pulsar-api-key` e `--pulsar-listener`
**Linhas 78, 124:** Passa `pulsar_client` para `ConfigReceiver` e `DispatcherManager`
Esta é a única localização que instancia diretamente um cliente Pulsar fora da camada de abstração.
### 2. Framework Base do Processador
**Localização:** `trustgraph-base/trustgraph/base/async_processor.py`
A classe base para todos os processadores fornece conectividade Pulsar:
**Linha 9:** `import _pulsar` (para tratamento de exceções)
**Linha 18:** `from . pubsub import PulsarClient`
**Linha 38:** Cria `pulsar_client_object = PulsarClient(**params)`
**Linhas 104-108:** Propriedades que expõem `pulsar_host` e `pulsar_client`
**Linha 250:** O método estático `add_args()` chama `PulsarClient.add_args(parser)` para argumentos da linha de comando
**Linhas 223-225:** Tratamento de exceções para `_pulsar.Interrupted`
Todos os processadores herdam de `AsyncProcessor`, tornando este o ponto de integração central.
### 3. Abstração do Consumidor
**Localização:** `trustgraph-base/trustgraph/base/consumer.py`
Consome mensagens de filas e invoca funções de tratamento:
**Importações do Pulsar:**
**Linha 12:** `from pulsar.schema import JsonSchema`
**Linha 13:** `import pulsar`
**Linha 14:** `import _pulsar`
**Uso específico do Pulsar:**
**Linhas 100, 102:** `pulsar.InitialPosition.Earliest` / `pulsar.InitialPosition.Latest`
**Linha 108:** Wrapper `JsonSchema(self.schema)`
**Linha 110:** `pulsar.ConsumerType.Shared`
**Linhas 104-111:** `self.client.subscribe()` com parâmetros específicos do Pulsar
**Linhas 143, 150, 65:** Métodos `consumer.unsubscribe()` e `consumer.close()`
**Linha 162:** Exceção `_pulsar.Timeout`
**Linhas 182, 205, 232:** `consumer.acknowledge()` / `consumer.negative_acknowledge()`
**Arquivo de especificação:** `trustgraph-base/trustgraph/base/consumer_spec.py`
**Linha 22:** Referencia `processor.pulsar_client`
### 4. Abstração do Produtor
**Localização:** `trustgraph-base/trustgraph/base/producer.py`
Envia mensagens para filas:
**Importações do Pulsar:**
**Linha 2:** `from pulsar.schema import JsonSchema`
**Uso específico do Pulsar:**
**Linha 49:** Wrapper `JsonSchema(self.schema)`
**Linhas 47-51:** `self.client.create_producer()` com parâmetros específicos do Pulsar (tópico, esquema, chunking_enabled)
**Linhas 31, 76:** Método `producer.close()`
**Linhas 64-65:** `producer.send()` com mensagem e propriedades
**Arquivo de especificação:** `trustgraph-base/trustgraph/base/producer_spec.py`
**Linha 18:** Referencia `processor.pulsar_client`
### 5. Abstração do Publicador
**Localização:** `trustgraph-base/trustgraph/base/publisher.py`
Publicação de mensagens assíncrona com buffer de fila:
**Importações do Pulsar:**
**Linha 2:** `from pulsar.schema import JsonSchema`
**Linha 6:** `import pulsar`
**Uso específico do Pulsar:**
**Linha 52:** Wrapper `JsonSchema(self.schema)`
**Linhas 50-54:** `self.client.create_producer()` com parâmetros específicos do Pulsar
**Linhas 101, 103:** `producer.send()` com mensagem e propriedades opcionais
**Linhas 106-107:** Métodos `producer.flush()` e `producer.close()`
### 6. Abstração do Assinante
**Localização:** `trustgraph-base/trustgraph/base/subscriber.py`
Fornece distribuição de mensagens para múltiplos destinatários a partir de filas:
**Importações do Pulsar:**
**Linha 6:** `from pulsar.schema import JsonSchema`
**Linha 8:** `import _pulsar`
**Uso específico do Pulsar:**
**Linha 55:** `JsonSchema(self.schema)` wrapper
**Linha 57:** `self.client.subscribe(**subscribe_args)`
**Linhas 101, 136, 160, 167-172:** Exceções do Pulsar: `_pulsar.Timeout`, `_pulsar.InvalidConfiguration`, `_pulsar.AlreadyClosed`
**Linhas 159, 166, 170:** Métodos do consumidor: `negative_acknowledge()`, `unsubscribe()`, `close()`
**Linhas 247, 251:** Reconhecimento de mensagens: `acknowledge()`, `negative_acknowledge()`
**Arquivo de especificação:** `trustgraph-base/trustgraph/base/subscriber_spec.py`
**Linha 19:** Referencia `processor.pulsar_client`
### 7. Sistema de Schema (Heart of Darkness)
**Localização:** `trustgraph-base/trustgraph/schema/`
Cada schema de mensagem no sistema é definido usando o framework de schema do Pulsar.
**Primitivos principais:** `schema/core/primitives.py`
**Linha 2:** `from pulsar.schema import Record, String, Boolean, Array, Integer`
Todos os schemas herdam da classe base do Pulsar `Record`
Todos os tipos de campo são tipos do Pulsar: `String()`, `Integer()`, `Boolean()`, `Array()`, `Map()`, `Double()`
**Schemas de exemplo:**
`schema/services/llm.py` (Linha 2): `from pulsar.schema import Record, String, Array, Double, Integer, Boolean`
`schema/services/config.py` (Linha 2): `from pulsar.schema import Record, Bytes, String, Boolean, Array, Map, Integer`
**Nomeação de tópicos:** `schema/core/topic.py`
**Linhas 2-3:** Formato do tópico: `{kind}://{tenant}/{namespace}/{topic}`
Esta estrutura de URI é específica do Pulsar (por exemplo, `persistent://tg/flow/config`)
**Impacto:**
Todas as definições de mensagens de solicitação/resposta em todo o código-fonte usam schemas do Pulsar
Isso inclui serviços para: config, flow, llm, prompt, query, storage, agent, collection, diagnosis, library, lookup, nlp_query, objects_query, retrieval, structured_query
As definições de schema são importadas e usadas extensivamente em todos os processadores e serviços
## Resumo
### Dependências do Pulsar por Categoria
1. **Instanciação do cliente:**
Direto: `gateway/service.py`
Abstrato: `async_processor.py``pubsub.py` (PulsarClient)
2. **Transporte de mensagens:**
Consumidor: `consumer.py`, `consumer_spec.py`
Produtor: `producer.py`, `producer_spec.py`
Publicador: `publisher.py`
Assinante: `subscriber.py`, `subscriber_spec.py`
3. **Sistema de schema:**
Tipos base: `schema/core/primitives.py`
Todos os schemas de serviço: `schema/services/*.py`
Nomeação de tópicos: `schema/core/topic.py`
4. **Conceitos específicos do Pulsar necessários:**
Mensagens baseadas em tópicos
Sistema de schema (Registro, tipos de campo)
Assinaturas compartilhadas
Reconhecimento de mensagens (positivo/negativo)
Posicionamento do consumidor (mais cedo/mais tarde)
Propriedades da mensagem
Posições iniciais e tipos de consumidor
Suporte de fragmentação
Tópicos persistentes vs não persistentes
### Desafios de Refatoração
A boa notícia: A camada de abstração (Consumidor, Produtor, Publicador, Assinante) fornece um encapsulamento limpo da maioria das interações do Pulsar.
Os desafios:
1. **Ubiquidade do sistema de schema:** Cada definição de mensagem usa `pulsar.schema.Record` e tipos de campo do Pulsar
2. **Enums específicos do Pulsar:** `InitialPosition`, `ConsumerType`
3. **Exceções do Pulsar:** `_pulsar.Timeout`, `_pulsar.Interrupted`, `_pulsar.InvalidConfiguration`, `_pulsar.AlreadyClosed`
4. **Assinaturas de método:** `acknowledge()`, `negative_acknowledge()`, `subscribe()`, `create_producer()`, etc.
5. **Formato de URI do tópico:** Estrutura do Pulsar `kind://tenant/namespace/topic`
### Próximos Passos
Para tornar a infraestrutura de pub/sub configurável, precisamos:
1. Criar uma interface de abstração para o sistema de cliente/schema
2. Abstrair enums e exceções específicos do Pulsar
3. Criar wrappers de schema ou definições de schema alternativas
4. Implementar a interface para sistemas Pulsar e sistemas alternativos (Kafka, RabbitMQ, Redis Streams, etc.)
5. Atualizar `pubsub.py` para ser configurável e suportar vários backends
6. Fornecer um caminho de migração para implantações existentes
## Abordagem Preliminar 1: Padrão Adapter com Camada de Tradução de Schema
### Principio Fundamental
O **sistema de schema** é o ponto de integração mais profundo - tudo o mais deriva dele. Precisamos resolver isso primeiro, ou teremos que reescrever todo o código-fonte.
### Estratégia: Encapsulamento Mínimo com Adapters
**1. Manter os esquemas Pulsar como a representação interna**
Não reescrever todas as definições de esquema
Os esquemas permanecem `pulsar.schema.Record` internamente
Usar adaptadores para traduzir na fronteira entre nosso código e o backend de publicação/assinatura
**2. Criar uma camada de abstração de publicação/assinatura:**
```
┌─────────────────────────────────────┐
│ Existing Code (unchanged) │
│ - Uses Pulsar schemas internally │
│ - Consumer/Producer/Publisher │
└──────────────┬──────────────────────┘
┌──────────────┴──────────────────────┐
│ PubSubFactory (configurable) │
│ - Creates backend-specific client │
└──────────────┬──────────────────────┘
┌──────┴──────┐
│ │
┌───────▼─────┐ ┌────▼─────────┐
│ PulsarAdapter│ │ KafkaAdapter │ etc...
│ (passthrough)│ │ (translates) │
└──────────────┘ └──────────────┘
```
**3. Defina interfaces abstratas:**
`PubSubClient` - conexão do cliente
`PubSubProducer` - envio de mensagens
`PubSubConsumer` - recebimento de mensagens
`SchemaAdapter` - tradução de esquemas Pulsar para/de JSON ou formatos específicos do backend
**4. Detalhes de implementação:**
Para o **adaptador Pulsar**: Quase transparente, tradução mínima
Para **outros backends** (Kafka, RabbitMQ, etc.):
Serializa objetos de registro Pulsar para JSON/bytes
Mapeia conceitos como:
`InitialPosition.Earliest/Latest` → auto.offset.reset do Kafka
`acknowledge()` → commit do Kafka
`negative_acknowledge()` → padrão de re-fila ou DLQ
URIs de tópicos → nomes de tópicos específicos do backend
### Análise
**Prós:**
✅ Alterações mínimas no código dos serviços existentes
✅ Os esquemas permanecem como estão (sem reescrita massiva)
✅ Caminho de migração gradual
✅ Os usuários do Pulsar não percebem diferença
✅ Novos backends adicionados via adaptadores
**Contras:**
⚠️ Ainda possui dependência do Pulsar (para definições de esquema)
⚠️ Alguma incompatibilidade na tradução de conceitos
### Consideração Alternativa
Crie um sistema de esquemas **TrustGraph** que seja agnóstico de pub/sub (usando dataclasses ou Pydantic), e então gere esquemas Pulsar/Kafka/etc a partir dele. Isso requer reescrever todos os arquivos de esquema e pode causar alterações disruptivas.
### Recomendação para a Versão 1
Comece com a **abordagem de adaptador** porque:
1. É pragmática - funciona com o código existente
2. Demonstra o conceito com risco mínimo
3. Pode evoluir para um sistema de esquemas nativo posteriormente, se necessário
4. Impulsionado por configuração: uma variável de ambiente alterna entre backends
## Abordagem da Versão 2: Sistema de Esquemas Agnostic de Backend com Dataclasses
### Conceito Central
Use **dataclasses** do Python como o formato de definição de esquema neutro. Cada backend de pub/sub fornece sua própria serialização/desserialização para dataclasses, eliminando a necessidade de que os esquemas Pulsar permaneçam no código-fonte.
### Polimorfismo de Esquema no Nível da Fábrica
Em vez de traduzir esquemas Pulsar, **cada backend fornece seu próprio tratamento de esquema** que funciona com dataclasses Python padrão.
### Fluxo do 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
```
### Fluxo do 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
```
### O que acontece nos bastidores
**Para o backend Pulsar:**
`create_producer()` → cria um produtor Pulsar com esquema JSON ou um registro gerado dinamicamente.
`send(request)` → serializa a classe de dados para o formato JSON/Pulsar e envia para o Pulsar.
`receive()` → recebe a mensagem do Pulsar, desserializa de volta para a classe de dados.
**Para o backend MQTT:**
`create_producer()` → conecta a um broker MQTT, não é necessário registro de esquema.
`send(request)` → converte a classe de dados para JSON e publica em um tópico MQTT.
`receive()` → assina um tópico MQTT e desserializa o JSON para a classe de dados.
**Para o backend Kafka:**
`create_producer()` → cria um produtor Kafka e registra o esquema Avro, se necessário.
`send(request)` → serializa a classe de dados para o formato Avro e envia para o Kafka.
`receive()` → recebe a mensagem do Kafka e desserializa o Avro de volta para a classe de dados.
### Pontos-chave do design
1. **Criação do objeto de esquema**: A instância da classe de dados (`TextCompletionRequest(...)`) é idêntica, independentemente do backend.
2. **O backend lida com a codificação**: Cada backend sabe como serializar sua classe de dados para o formato de transmissão.
3. **Definição do esquema na criação**: Ao criar o produtor/consumidor, você especifica o tipo de esquema.
4. **Segurança de tipo preservada**: Você recebe um objeto `TextCompletionRequest` adequado, não um dicionário.
5. **Nenhum vazamento do backend**: O código da aplicação nunca importa bibliotecas específicas do backend.
### Exemplo de transformação
**Atual (específico para Pulsar):**
```python
# schema/services/llm.py
from pulsar.schema import Record, String, Boolean, Integer
class TextCompletionRequest(Record):
system = String()
prompt = String()
streaming = Boolean()
```
**Novo (Independente do backend):**
```python
# schema/services/llm.py
from dataclasses import dataclass
@dataclass
class TextCompletionRequest:
system: str
prompt: str
streaming: bool = False
```
### Integração com o Backend
Cada backend lida com a serialização/desserialização de dataclasses:
**Backend Pulsar:**
Gera classes `pulsar.schema.Record` dinamicamente a partir de dataclasses
Ou serializa dataclasses para JSON e usa o esquema JSON do Pulsar
Mantém a compatibilidade com implantações Pulsar existentes
**Backend MQTT/Redis:**
Serialização direta de instâncias de dataclass para JSON
Use `dataclasses.asdict()` / `from_dict()`
Leve, não requer registro de esquema
**Backend Kafka:**
Gera esquemas Avro a partir de definições de dataclass
Use o registro de esquema da Confluent
Serialização com segurança de tipo com suporte à evolução do esquema
### Arquitetura
```
┌─────────────────────────────────────┐
│ 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 │ │ │
└─────────────────┘ └───────────────────┘
```
### Detalhes de Implementação
**1. Definições de esquema:** Dataclasses simples com dicas de tipo
`str`, `int`, `bool`, `float` para tipos primitivos
`list[T]` para arrays
`dict[str, T]` para mapas
Dataclasses aninhados para tipos complexos
**2. Cada backend fornece:**
Serializador: `dataclass → bytes/wire format`
Deserializador: `bytes/wire format → dataclass`
Registro de esquema (se necessário, como Pulsar/Kafka)
**3. Abstração de consumidor/produtor:**
Já existe (consumer.py, producer.py)
Atualizar para usar a serialização do backend
Remover importações diretas do Pulsar
**4. Mapeamentos 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`
### Caminho de Migração
1. **Criar versões de dataclass** de todos os esquemas em `trustgraph/schema/`
2. **Atualizar classes de backend** (Consumer, Producer, Publisher, Subscriber) para usar a serialização fornecida pelo backend
3. **Implementar PulsarBackend** com esquema JSON ou geração dinâmica de Record
4. **Testar com Pulsar** para garantir a compatibilidade com versões anteriores com implantações existentes
5. **Adicionar novos backends** (MQTT, Kafka, Redis, etc.) conforme necessário
6. **Remover importações do Pulsar** de arquivos de esquema
### Benefícios
**Nenhuma dependência de pub/sub** nas definições de esquema
**Python padrão** - fácil de entender, tipar, documentar
**Ferramentas modernas** - funciona com mypy, preenchimento automático de IDE, linters
**Otimizado para backend** - cada backend usa a serialização nativa
**Sem sobrecarga de tradução** - serialização direta, sem adaptadores
**Segurança de tipo** - objetos reais com tipos adequados
**Validação fácil** - pode usar Pydantic, se necessário
### Desafios e Soluções
**Desafio:** O `Record` do Pulsar tem validação de campo em tempo de execução
**Solução:** Use dataclasses Pydantic para validação, se necessário, ou recursos de dataclass Python 3.10+ com `__post_init__`
**Desafio:** Alguns recursos específicos do Pulsar (como o tipo `Bytes`)
**Solução:** Mapear para o tipo `bytes` na dataclass, o backend lida com a codificação apropriadamente
**Desafio:** Nomenclatura de tópicos (`persistent://tenant/namespace/topic`)
**Solução:** Abstrair nomes de tópicos em definições de esquema, o backend converte para o formato adequado
**Desafio:** Evolução e versionamento de esquema
**Solução:** Cada backend lida com isso de acordo com suas capacidades (versões de esquema do Pulsar, registro de esquema do Kafka, etc.)
**Desafio:** Tipos complexos aninhados
**Solução:** Use dataclasses aninhadas, os backends serializam/desserializam recursivamente
### Decisões de Design
1. **Dataclasses simples ou Pydantic?**
✅ **Decisão: Usar dataclasses Python simples**
Mais simples, sem dependências adicionais
Validação não é necessária na prática
Mais fácil de entender e manter
2. **Evolução de esquema:**
✅ **Decisão: Nenhum mecanismo de versionamento necessário**
Os esquemas são estáveis e duradouros
As atualizações normalmente adicionam novos campos (compatíveis com versões anteriores)
Os backends lidam com a evolução do esquema de acordo com suas capacidades
3. **Compatibilidade com versões anteriores:**
✅ **Decisão: Alteração de versão principal, compatibilidade com versões anteriores não é necessária**
Será uma alteração disruptiva com instruções de migração
A separação limpa permite um melhor design
Um guia de migração será fornecido para implantações existentes
4. **Tipos aninhados e estruturas complexas:**
✅ **Decisão: Usar dataclasses aninhadas naturalmente**
Dataclasses Python lidam com o aninhamento perfeitamente
`list[T]` para arrays, `dict[K, V]` para mapas
Backends serializam/desserializam recursivamente
Exemplo:
```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 padrão e campos opcionais:**
✅ **Decisão: Mistura de campos obrigatórios, valores padrão e campos opcionais**
Campos obrigatórios: Sem valor padrão
Campos com valores padrão: Sempre presentes, possuem um valor padrão razoável
Campos verdadeiramente opcionais: `T | None = None`, omitidos da serialização quando `None`
Exemplo:
```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 serialização importante:**
Quando `metadata = None`:
```json
{
"system": "...",
"prompt": "...",
"streaming": false
// metadata field NOT PRESENT
}
```
Quando `metadata = {}` (explicitamente vazio):
```json
{
"system": "...",
"prompt": "...",
"streaming": false,
"metadata": {} // Field PRESENT but empty
}
```
**Diferença chave:**
`None` → campo ausente do JSON (não serializado)
Valor vazio (`{}`, `[]`, `""`) → campo presente com valor vazio
Isso importa semanticamente: "não fornecido" vs "explicitamente vazio"
Os backends de serialização devem ignorar os campos `None`, não codificá-los como `null`
## Abordagem Rascunho 3: Detalhes de Implementação
### Formato Genérico de Nomes de Filas
Substitua os nomes de filas específicos do backend por um formato genérico que os backends possam mapear adequadamente.
**Formato:** `{qos}/{tenant}/{namespace}/{queue-name}`
Onde:
`qos`: Nível de Qualidade de Serviço
`q0` = melhor esforço (enviar e esquecer, sem confirmação)
`q1` = pelo menos uma vez (requer confirmação)
`q2` = exatamente uma vez (confirmação de duas fases)
`tenant`: Agrupamento lógico para multi-inquilinato
`namespace`: Sub-agrupamento dentro do inquilino
`queue-name`: Nome real da fila/tópico
**Exemplos:**
```
q1/tg/flow/text-completion-requests
q2/tg/config/config-push
q0/tg/metrics/stats
```
### Mapeamento de Tópicos do Backend
Cada backend mapeia o formato genérico para o seu formato nativo:
**Backend do 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 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
```
### Função de Auxílio de Tópico Atualizada
```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}"
```
### Configuração e Inicialização
**Argumentos de Linha de Comando + Variáveis de Ambiente:**
```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)'
)
```
**Função 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 em 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...
```
### Interface 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."""
...
```
### Refatoração das Classes Existentes
As classes existentes `Consumer`, `Producer`, `Publisher`, `Subscriber` permanecem em grande parte inalteradas:
**Responsabilidades atuais (manter):**
Modelo de threading assíncrono e grupos de tarefas
Lógica de reconexão e tratamento de repetições
Coleta de métricas
Limitação de taxa
Gerenciamento de concorrência
**Alterações necessárias:**
Remover importações diretas do Pulsar (`pulsar.schema`, `pulsar.InitialPosition`, etc.)
Aceitar `BackendProducer`/`BackendConsumer` em vez do cliente Pulsar
Delegar as operações reais de publicação/assinatura para instâncias de backend
Mapear conceitos genéricos para chamadas de backend
**Exemplo de refatoração:**
```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)
```
### Comportamentos Específicos do Backend
**Backend Pulsar:**
Mapeia `q0``non-persistent://`, `q1`/`q2``persistent://`
Suporta todos os tipos de consumidores (compartilhado, exclusivo, failover)
Suporta posição inicial (earliest/latest)
Reconhecimento nativo de mensagens
Suporte para registro de esquema
**Backend MQTT:**
Mapeia `q0`/`q1`/`q2` → Níveis de QoS MQTT 0/1/2
Inclui tenant/namespace no caminho do tópico para fins de namespace
Gera automaticamente IDs de cliente a partir de nomes de inscrição
Ignora a posição inicial (sem histórico de mensagens no MQTT básico)
Ignora o tipo de consumidor (o MQTT usa IDs de cliente, não grupos de consumidores)
Modelo de publicação/assinatura simples
### Resumo das Decisões de Design
1. ✅ **Nomeação genérica de filas**: Formato `qos/tenant/namespace/queue-name`
2. ✅ **QoS no ID da fila**: Determinado pela definição da fila, não pela configuração
3. ✅ **Reconexão**: Tratada pelas classes Consumer/Producer, não pelos backends
4. ✅ **Tópicos MQTT**: Incluem tenant/namespace para namespace adequado
5. ✅ **Histórico de mensagens**: O MQTT ignora o parâmetro `initial_position` (melhoria futura)
6. ✅ **IDs de cliente**: O backend MQTT gera automaticamente a partir do nome da inscrição
### Melhorias Futuras
**Histórico de mensagens MQTT:**
Poderia adicionar uma camada de persistência opcional (por exemplo, mensagens retidas, armazenamento externo)
Permitiria suportar `initial_position='earliest'`
Não é necessário para a implementação inicial

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,271 @@
---
layout: default
title: "Explicabilidade em Tempo de Consulta"
parent: "Portuguese (Beta)"
---
# Explicabilidade em Tempo 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.
## Status
Implementado
## Visão Geral
Esta especificação descreve como o GraphRAG registra e comunica dados de explicabilidade durante a execução da consulta. O objetivo é a rastreabilidade completa: desde a resposta final, passando pelas arestas selecionadas, até aos documentos de origem.
A explicabilidade em tempo de consulta captura o que o pipeline do GraphRAG fez durante o raciocínio. Isso se conecta à rastreabilidade em tempo de extração, que registra a origem dos fatos do grafo de conhecimento.
## Terminologia
| Termo | Definição |
|------|------------|
| **Explicabilidade** | O registro de como um resultado foi derivado |
| **Sessão** | Uma única execução de consulta do GraphRAG |
| **Seleção de Arestas** | Seleção de arestas relevantes impulsionada por LLM, com raciocínio |
| **Cadeia de Rastreabilidade** | Caminho de aresta → trecho → página → documento |
## Arquitetura
### Fluxo de Explicabilidade
```
GraphRAG Query
├─► Session Activity
│ └─► Query text, timestamp
├─► Retrieval Entity
│ └─► All edges retrieved from subgraph
├─► Selection Entity
│ └─► Selected edges with LLM reasoning
│ └─► Each edge links to extraction provenance
└─► Answer Entity
└─► Reference to synthesized response (in librarian)
```
### Pipeline de GraphRAG em Duas Etapas
1. **Seleção de Arestas**: O LLM seleciona as arestas relevantes do subgrafo, fornecendo a justificativa para cada uma.
2. **Síntese**: O LLM gera a resposta a partir das arestas selecionadas.
Essa separação permite a explicabilidade - sabemos exatamente quais arestas contribuíram.
### Armazenamento
Triplas de explicabilidade armazenadas em uma coleção configurável (padrão: `explainability`).
Utiliza a ontologia PROV-O para relações de procedência.
Reificação RDF-star para referências de arestas.
O conteúdo da resposta é armazenado no serviço de bibliotecário (não inline - muito grande).
### Streaming em Tempo Real
Os eventos de explicabilidade são transmitidos ao cliente enquanto a consulta é executada:
1. Sessão criada → evento emitido.
2. Arestas recuperadas → evento emitido.
3. Arestas selecionadas com justificativa → evento emitido.
4. Resposta sintetizada → evento emitido.
O cliente recebe `explain_id` e `explain_collection` para buscar detalhes completos.
## Estrutura de URI
Todos os URIs usam o namespace `urn:trustgraph:` com UUIDs:
| Entidade | Padrão de URI |
|--------|-------------|
| Sessão | `urn:trustgraph:session:{uuid}` |
| Recuperação | `urn:trustgraph:prov:retrieval:{uuid}` |
| Seleção | `urn:trustgraph:prov:selection:{uuid}` |
| Resposta | `urn:trustgraph:prov:answer:{uuid}` |
| Seleção de Aresta | `urn:trustgraph:prov:edge:{uuid}:{index}` |
## Modelo RDF (PROV-O)
### Atividade da Sessão
```turtle
<session-uri> a prov:Activity ;
rdfs:label "GraphRAG query session" ;
prov:startedAtTime "2024-01-15T10:30:00Z" ;
tg:query "What was the War on Terror?" .
```
### Entidade de Recuperação
```turtle
<retrieval-uri> a prov:Entity ;
rdfs:label "Retrieved edges" ;
prov:wasGeneratedBy <session-uri> ;
tg:edgeCount 50 .
```
### Entidade de Seleção
```turtle
<selection-uri> a prov:Entity ;
rdfs:label "Selected edges" ;
prov:wasDerivedFrom <retrieval-uri> ;
tg:selectedEdge <edge-sel-0> ;
tg:selectedEdge <edge-sel-1> .
<edge-sel-0> tg:edge << <s> <p> <o> >> ;
tg:reasoning "This edge establishes the key relationship..." .
```
### Entidade de Resposta
```turtle
<answer-uri> a prov:Entity ;
rdfs:label "GraphRAG answer" ;
prov:wasDerivedFrom <selection-uri> ;
tg:document <urn:trustgraph:answer:{uuid}> .
```
A `tg:document` referencia a resposta armazenada no serviço de bibliotecário.
## Constantes do Namespace
Definido em `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 Mensagem
| message_type | Propósito |
|--------------|---------|
| `chunk` | Texto de resposta (em fluxo ou final) |
| `explain` | Evento de explicabilidade com referência IRI |
### Ciclo de Vida da Sessão
1. Múltiplas mensagens `explain` (sessão, recuperação, seleção, resposta)
2. Múltiplas mensagens `chunk` (resposta em fluxo)
3. Mensagem `chunk` final com `end_of_session=True`
## Formato de Seleção de Arestas
O LLM retorna JSONL com as arestas selecionadas:
```jsonl
{"id": "edge-hash-1", "reasoning": "This edge shows the key relationship..."}
{"id": "edge-hash-2", "reasoning": "Provides supporting evidence..."}
```
O `id` é um hash de `(labeled_s, labeled_p, labeled_o)` calculado por `edge_id()`.
## Preservação de URIs
### O Problema
O GraphRAG exibe rótulos legíveis para humanos para o LLM, mas a explicabilidade precisa de URIs originais para rastreamento de origem.
### Solução
`get_labelgraph()` retorna ambos:
`labeled_edges`: Lista de `(label_s, label_p, label_o)` para o LLM
`uri_map`: Dicionário mapeando `edge_id(labels)``(uri_s, uri_p, uri_o)`
Ao armazenar dados de explicabilidade, os URIs de `uri_map` são usados.
## Rastreamento de Origem
### Do Borda à Fonte
As arestas selecionadas podem ser rastreadas de volta aos documentos de origem:
1. Consulta para o subgrafo contendo: `?subgraph tg:contains <<s p o>>`
2. Siga a cadeia `prov:wasDerivedFrom` até o documento raiz
3. Cada etapa na cadeia: chunk → página → documento
### Suporte de Triplas Citadas do Cassandra
O serviço de consulta do Cassandra suporta a correspondência de triplas citadas:
```python
# In get_term_value():
elif term.type == TRIPLE:
return serialize_triple(term.triple)
```
Isso permite consultas como:
```
?subgraph tg:contains <<http://example.org/s http://example.org/p "value">>
```
## Uso da Interface de Linha de Comando (CLI)
```bash
tg-invoke-graph-rag --explainable -q "What was the War on Terror?"
```
### Formato de Saída
```
[session] urn:trustgraph:session:abc123
[retrieval] urn:trustgraph:prov:retrieval:abc123
[selection] urn:trustgraph:prov:selection:abc123
Selected 12 edge(s)
Edge: (Guantanamo, definition, A detention facility...)
Reason: Directly connects Guantanamo to the War on Terror
Source: Chunk 1 → Page 2 → Beyond the Vigilant State
[answer] urn:trustgraph:prov:answer:abc123
Based on the provided knowledge statements...
```
### Recursos
Eventos de explicabilidade em tempo real durante a consulta.
Resolução de rótulos para componentes de borda via `rdfs:label`.
Rastreamento da cadeia de origem via `prov:wasDerivedFrom`.
Cache de rótulos para evitar consultas repetidas.
## Arquivos Implementados
| Arquivo | Propósito |
|------|---------|
| `trustgraph-base/trustgraph/provenance/uris.py` | Geradores de URI |
| `trustgraph-base/trustgraph/provenance/namespaces.py` | Constantes de namespace RDF |
| `trustgraph-base/trustgraph/provenance/triples.py` | Construtores de triplas |
| `trustgraph-base/trustgraph/schema/services/retrieval.py` | Esquema GraphRagResponse |
| `trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py` | Núcleo GraphRAG com preservação de URI |
| `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` | Serviço com integração de bibliotecário |
| `trustgraph-flow/trustgraph/query/triples/cassandra/service.py` | Suporte para consultas de triplas entre aspas |
| `trustgraph-cli/trustgraph/cli/invoke_graph_rag.py` | CLI com exibição de explicabilidade |
## Referências
PROV-O (Ontologia de Proveniência W3C): https://www.w3.org/TR/prov-o/
RDF-star: https://w3c.github.io/rdf-star/
Proveniência no momento da extração: `docs/tech-specs/extraction-time-provenance.md`

View file

@ -0,0 +1,296 @@
---
layout: default
title: "Especificação Técnica de Suporte a Streaming RAG"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Suporte a Streaming 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.
## Visão Geral
Esta especificação descreve a adição de suporte a streaming aos serviços GraphRAG e DocumentRAG, permitindo respostas em tempo real, token por token, para consultas de grafos de conhecimento e recuperação de documentos. Isso estende a arquitetura de streaming existente já implementada para serviços de preenchimento de texto, prompt e agente LLM.
## Objetivos
**Experiência de streaming consistente**: Fornecer a mesma experiência de streaming em todos os serviços TrustGraph.
**Alterações mínimas na API**: Adicionar suporte a streaming com um único sinalizador `streaming`, seguindo padrões estabelecidos.
**Compatibilidade com versões anteriores**: Manter o comportamento padrão existente sem streaming.
**Reutilizar infraestrutura existente**: Aproveitar o streaming do PromptClient já implementado.
**Suporte a gateway**: Permitir o streaming através do gateway WebSocket para aplicativos cliente.
## Contexto
Serviços de streaming atualmente implementados:
**Serviço de preenchimento de texto LLM**: Fase 1 - streaming de provedores LLM.
**Serviço de prompt**: Fase 2 - streaming através de modelos de prompt.
**Serviço de agente**: Fase 3-4 - streaming de respostas ReAct com fragmentos incrementais de pensamento/observação/resposta.
Limitações atuais para serviços RAG:
GraphRAG e DocumentRAG suportam apenas respostas bloqueadas.
Os usuários devem esperar pela resposta completa do LLM antes de ver qualquer saída.
Má experiência do usuário para respostas longas de grafos de conhecimento ou consultas de documentos.
Experiência inconsistente em comparação com outros serviços TrustGraph.
Esta especificação aborda essas lacunas adicionando suporte a streaming ao GraphRAG e ao DocumentRAG. Ao permitir respostas token por token, o TrustGraph pode:
Fornecer uma experiência de streaming consistente em todos os tipos de consulta.
Reduzir a latência percebida para consultas RAG.
Permitir um melhor feedback de progresso para consultas de longa duração.
Suportar a exibição em tempo real em aplicativos cliente.
## Design Técnico
### Arquitetura
A implementação de streaming RAG aproveita a infraestrutura existente:
1. **Streaming do PromptClient** (Já implementado)
`kg_prompt()` e `document_prompt()` já aceitam parâmetros `streaming` e `chunk_callback`.
Isso chama `prompt()` internamente com suporte a streaming.
Não são necessárias alterações no PromptClient.
Módulo: `trustgraph-base/trustgraph/base/prompt_client.py`
2. **Serviço GraphRAG** (Precisa de passagem de parâmetro de streaming)
Adicionar parâmetro `streaming` ao método `query()`.
Passar o sinalizador de streaming e os callbacks para `prompt_client.kg_prompt()`.
O esquema GraphRagRequest precisa do campo `streaming`.
Módulos:
`trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py`
`trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` (Processador)
`trustgraph-base/trustgraph/schema/graph_rag.py` (Esquema de solicitação)
`trustgraph-flow/trustgraph/gateway/dispatch/graph_rag.py` (Gateway)
3. **Serviço DocumentRAG** (Precisa de passagem de parâmetro de streaming)
Adicionar parâmetro `streaming` ao método `query()`.
Passar o sinalizador de streaming e os callbacks para `prompt_client.document_prompt()`.
O esquema DocumentRagRequest precisa do campo `streaming`.
Módulos:
`trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py`
`trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` (Processador)
`trustgraph-base/trustgraph/schema/document_rag.py` (Esquema de solicitação)
`trustgraph-flow/trustgraph/gateway/dispatch/document_rag.py` (Gateway)
### Fluxo de Dados
**Não streaming (atual)**:
```
Client → Gateway → RAG Service → PromptClient.kg_prompt(streaming=False)
Prompt Service → LLM
Complete response
Client ← Gateway ← RAG Service ← Response
```
**Streaming (proposto):**
```
Client → Gateway → RAG Service → PromptClient.kg_prompt(streaming=True, chunk_callback=cb)
Prompt Service → LLM (streaming)
Chunk → callback → RAG Response (chunk)
↓ ↓
Client ← Gateway ← ────────────────────────────────── Response stream
```
### APIs
**Alterações no GraphRAG**:
1. **GraphRag.query()** - Adicionar parâmetros de streaming
```python
async def query(
self, query, user, collection,
verbose=False, streaming=False, chunk_callback=None # NEW
):
# ... existing entity/triple retrieval ...
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** - Adicionar campo de streaming.
```python
class GraphRagRequest(Record):
query = String()
user = String()
collection = String()
streaming = Boolean() # NEW
```
3. **Esquema GraphRagResponse** - Adicionar campos de streaming (seguir o padrão do Agente).
```python
class GraphRagResponse(Record):
response = String() # Legacy: complete response
chunk = String() # NEW: streaming chunk
end_of_stream = Boolean() # NEW: indicates last chunk
```
4. **Processador** - Permitir a passagem de dados em fluxo contínuo.
```python
async def handle(self, msg):
# ... existing code ...
async def send_chunk(chunk):
await self.respond(GraphRagResponse(
chunk=chunk,
end_of_stream=False,
response=None
))
if request.streaming:
full_response = await self.rag.query(
query=request.query,
user=request.user,
collection=request.collection,
streaming=True,
chunk_callback=send_chunk
)
# Send final message
await self.respond(GraphRagResponse(
chunk=None,
end_of_stream=True,
response=full_response
))
else:
# Existing non-streaming path
response = await self.rag.query(...)
await self.respond(GraphRagResponse(response=response))
```
**Alterações no DocumentRAG:**
Padrão idêntico ao GraphRAG:
1. Adicionar parâmetros `streaming` e `chunk_callback` a `DocumentRag.query()`
2. Adicionar campo `streaming` a `DocumentRagRequest`
3. Adicionar campos `chunk` e `end_of_stream` a `DocumentRagResponse`
4. Atualizar o Processador para lidar com streaming com callbacks
**Alterações no Gateway:**
Tanto `graph_rag.py` quanto `document_rag.py` no gateway/dispatch precisam de atualizações para encaminhar trechos de streaming para o websocket:
```python
async def handle(self, message, session, websocket):
# ... existing code ...
if request.streaming:
async def recipient(resp):
if resp.chunk:
await websocket.send(json.dumps({
"id": message["id"],
"response": {"chunk": resp.chunk},
"complete": resp.end_of_stream
}))
return resp.end_of_stream
await self.rag_client.request(request, recipient=recipient)
else:
# Existing non-streaming path
resp = await self.rag_client.request(request)
await websocket.send(...)
```
### Detalhes de Implementação
**Ordem de implementação**:
1. Adicionar campos de esquema (Request + Response para ambos os serviços RAG)
2. Atualizar os métodos GraphRag.query() e DocumentRag.query()
3. Atualizar Processadores para lidar com streaming
4. Atualizar manipuladores de despacho do Gateway
5. Adicionar `--no-streaming` flags a `tg-invoke-graph-rag` e `tg-invoke-document-rag` (streaming habilitado por padrão, seguindo o padrão da CLI do agente)
**Padrão de callback**:
Seguir o mesmo padrão de callback assíncrono estabelecido no streaming do Agente:
O Processador define o `async def send_chunk(chunk)` callback
Passa o callback para o serviço RAG
O serviço RAG passa o callback para o PromptClient
O PromptClient invoca o callback para cada chunk do LLM
O Processador envia uma mensagem de resposta de streaming para cada chunk
**Tratamento de erros**:
Erros durante o streaming devem enviar uma resposta de erro com `end_of_stream=True`
Seguir os padrões existentes de propagação de erros do streaming do Agente
## Considerações de Segurança
Não há novas considerações de segurança além dos serviços RAG existentes:
As respostas de streaming usam o mesmo isolamento de usuário/coleção
Não há alterações na autenticação ou autorização
Os limites de chunk não expõem dados sensíveis
## Considerações de Desempenho
**Benefícios**:
Latência percebida reduzida (os primeiros tokens chegam mais rápido)
Melhor experiência do usuário para respostas longas
Menor uso de memória (não é necessário armazenar em buffer a resposta completa)
**Preocupações potenciais**:
Mais mensagens Pulsar para respostas de streaming
Ligeiramente maior uso de CPU para a sobrecarga de chunking/callback
Mitigado por: o streaming é opcional, o padrão permanece não-streaming
**Considerações de teste**:
Testar com grafos de conhecimento grandes (muitos triplos)
Testar com muitos documentos recuperados
Medir a sobrecarga do streaming versus não-streaming
## Estratégia de Teste
**Testes unitários**:
Testar GraphRag.query() com streaming=True/False
Testar DocumentRag.query() com streaming=True/False
Simular o PromptClient para verificar as invocações de callback
**Testes de integração**:
Testar o fluxo completo de streaming do GraphRAG (semelhante aos testes existentes de streaming do agente)
Testar o fluxo completo de streaming do DocumentRAG
Testar o encaminhamento de streaming do Gateway
Testar a saída de streaming da CLI
**Testes manuais**:
`tg-invoke-graph-rag -q "What is machine learning?"` (streaming por padrão)
`tg-invoke-document-rag -q "Summarize the documents about AI"` (streaming por padrão)
`tg-invoke-graph-rag --no-streaming -q "..."` (testar o modo não-streaming)
Verificar se a saída incremental aparece no modo de streaming
## Plano de Migração
Nenhuma migração necessária:
O streaming é opcional por meio do parâmetro `streaming` (o padrão é Falso)
Os clientes existentes continuam a funcionar sem alterações
Novos clientes podem optar por usar o streaming
## Cronograma
Tempo estimado de implementação: 4-6 horas
Fase 1 (2 horas): suporte de streaming do GraphRAG
Fase 2 (2 horas): suporte de streaming do DocumentRAG
Fase 3 (1-2 horas): atualizações do Gateway e flags da CLI
Testes: integrados em cada fase
## Perguntas Abertas
Devemos adicionar suporte de streaming ao serviço de consulta NLP também?
Queremos transmitir etapas intermediárias (por exemplo, "Recuperando entidades...", "Consultando o grafo...") ou apenas a saída do LLM?
As respostas do GraphRAG/DocumentRAG devem incluir metadados do chunk (por exemplo, número do chunk, total esperado)?
## Referências
Implementação existente: `docs/tech-specs/streaming-llm-responses.md`
Streaming do Agente: `trustgraph-flow/trustgraph/agent/react/agent_manager.py`
Streaming do PromptClient: `trustgraph-base/trustgraph/base/prompt_client.py`

View file

@ -0,0 +1,99 @@
---
layout: default
title: "Proposta de Refatoração do Diretório de Esquemas"
parent: "Portuguese (Beta)"
---
# Proposta de Refatoração do Diretório 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 Atuais
1. **Estrutura plana** - Todos os esquemas em um único diretório dificultam a compreensão das relações.
2. **Preocupações misturadas** - Tipos principais, objetos de domínio e contratos de API todos misturados.
3. **Nomenclatura pouco clara** - Arquivos como "object.py", "types.py", "topic.py" não indicam claramente seu propósito.
4. **Ausência de camadas claras** - Não é fácil ver o que depende do quê.
## Estrutura Proposta
```
trustgraph-base/trustgraph/schema/
├── __init__.py
├── core/ # Core primitive types used everywhere
│ ├── __init__.py
│ ├── primitives.py # Error, Value, Triple, Field, RowSchema
│ ├── metadata.py # Metadata record
│ └── topic.py # Topic utilities
├── knowledge/ # Knowledge domain models and extraction
│ ├── __init__.py
│ ├── graph.py # EntityContext, EntityEmbeddings, Triples
│ ├── document.py # Document, TextDocument, Chunk
│ ├── knowledge.py # Knowledge extraction types
│ ├── embeddings.py # All embedding-related types (moved from multiple files)
│ └── nlp.py # Definition, Topic, Relationship, Fact types
└── services/ # Service request/response contracts
├── __init__.py
├── llm.py # TextCompletion, Embeddings, Tool requests/responses
├── retrieval.py # GraphRAG, DocumentRAG queries/responses
├── query.py # GraphEmbeddingsRequest/Response, DocumentEmbeddingsRequest/Response
├── agent.py # Agent requests/responses
├── flow.py # Flow requests/responses
├── prompt.py # Prompt service requests/responses
├── config.py # Configuration service
├── library.py # Librarian service
└── lookup.py # Lookup service
```
## Principais Alterações
1. **Organização hierárquica** - Separação clara entre tipos principais, modelos de conhecimento e contratos de serviço.
2. **Melhores nomes:**
`types.py``core/primitives.py` (propósito mais claro)
`object.py` → Divisão entre arquivos apropriados com base no conteúdo real.
`documents.py``knowledge/document.py` (singular, consistente)
`models.py``services/llm.py` (mais claro que tipo de modelos)
`prompt.py` → Divisão: partes de serviço para `services/prompt.py`, tipos de dados para `knowledge/nlp.py`
3. **Agrupamento lógico:**
Todos os tipos de incorporação consolidados em `knowledge/embeddings.py`
Todos os contratos de serviço relacionados a LLM em `services/llm.py`
Separação clara de pares de solicitação/resposta no diretório de serviços.
Tipos de extração de conhecimento agrupados com outros modelos de domínio de conhecimento.
4. **Clareza das dependências:**
Tipos principais não têm dependências.
Modelos de conhecimento dependem apenas do núcleo.
Contratos de serviço podem depender tanto do núcleo quanto dos modelos de conhecimento.
## Benefícios da Migração
1. **Navegação mais fácil** - Os desenvolvedores podem encontrar rapidamente o que precisam.
2. **Melhor modularidade** - Limites claros entre diferentes aspectos.
3. **Importações mais simples** - Caminhos de importação mais intuitivos.
4. **Preparado para o futuro** - Fácil de adicionar novos tipos de conhecimento ou serviços sem causar desordem.
## Exemplos de Alterações de Importação
```python
# Before
from trustgraph.schema import Error, Triple, GraphEmbeddings, TextCompletionRequest
# After
from trustgraph.schema.core import Error, Triple
from trustgraph.schema.knowledge import GraphEmbeddings
from trustgraph.schema.services import TextCompletionRequest
```
## Notas de Implementação
1. Mantenha a compatibilidade com versões anteriores, mantendo as importações no diretório raiz `__init__.py`.
2. Mova os arquivos gradualmente, atualizando as importações conforme necessário.
3. Considere adicionar um `legacy.py` que importe tudo para o período de transição.
4. Atualize a documentação para refletir a nova estrutura.
<function_calls>
<invoke name="TodoWrite">
<parameter name="todos">[{"id": "1", "content": "Examinar a estrutura de diretórios do esquema atual", "status": "completed", "priority": "high"}, {"id": "2", "content": "Analisar os arquivos de esquema e seus propósitos", "status": "completed", "priority": "high"}, {"id": "3", "content": "Propor nomenclatura e estrutura aprimoradas", "status": "completed", "priority": "high"}]

View file

@ -0,0 +1,578 @@
---
layout: default
title: "Especificação Técnica de Respostas de LLM em Streaming"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Respostas de LLM em 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.
## Visão Geral
Esta especificação descreve a implementação do suporte a streaming para respostas de LLM
no TrustGraph. O streaming permite a entrega em tempo real de tokens gerados
à medida que são produzidos pelo LLM, em vez de esperar pela geração completa
da resposta.
Esta implementação suporta os seguintes casos de uso:
1. **Interfaces de Usuário em Tempo Real**: Transmita tokens para a interface do usuário à medida que são gerados,
fornecendo feedback visual imediato.
2. **Tempo de Primeiro Token Reduzido**: Os usuários veem a saída começando imediatamente
em vez de esperar pela geração completa.
3. **Tratamento de Respostas Longas**: Lidar com saídas muito longas que, de outra forma,
poderiam causar timeout ou exceder os limites de memória.
4. **Aplicações Interativas**: Permitir interfaces de bate-papo e agentes responsivas.
## Objetivos
**Compatibilidade com Versões Anteriores**: Os clientes existentes que não utilizam streaming continuam a funcionar
sem modificação.
**Design de API Consistente**: O streaming e o uso sem streaming utilizam os mesmos padrões de esquema
com mínima divergência.
**Flexibilidade do Provedor**: Suporte ao streaming quando disponível, com uma alternativa
suave quando não disponível.
**Implementação Gradual**: Implementação incremental para reduzir o risco.
**Suporte de Ponta a Ponta**: Streaming do provedor de LLM até as aplicações do cliente
via Pulsar, Gateway API e Python API.
## Contexto
### Arquitetura Atual
O fluxo atual de conclusão de texto de LLM opera da seguinte forma:
1. O cliente envia `TextCompletionRequest` com os campos `system` e `prompt`.
2. O serviço de LLM processa a solicitação e espera pela geração completa.
3. Um único `TextCompletionResponse` é retornado com a string `response` completa.
Esquema atual (`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()
```
### Limitações Atuais
**Latência**: Os usuários devem esperar pela geração completa antes de ver qualquer resultado.
**Risco de Timeout**: Gerações longas podem exceder os limites de tempo de espera do cliente.
**Má Experiência do Usuário**: A falta de feedback durante a geração cria a percepção de lentidão.
**Uso de Recursos**: As respostas completas devem ser armazenadas em memória.
Esta especificação aborda essas limitações, permitindo a entrega incremental de respostas, mantendo total compatibilidade com versões anteriores.
## Design Técnico
### Fase 1: Infraestrutura
A Fase 1 estabelece a base para o streaming, modificando esquemas, APIs e ferramentas de linha de comando.
#### Alterações no Esquema
##### Esquema LLM (`trustgraph-base/trustgraph/schema/services/llm.py`)
**Alterações na Requisição:**
```python
class TextCompletionRequest(Record):
system = String()
prompt = String()
streaming = Boolean() # NEW: Default false for backward compatibility
```
`streaming`: Quando `true`, solicita a entrega de resposta em fluxo.
Padrão: `false` (o comportamento existente é preservado).
**Alterações na Resposta:**
```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`: Quando `true`, indica que esta é a resposta final (ou única).
Para solicitações não em fluxo contínuo: Resposta única com `end_of_stream=true`.
Para solicitações em fluxo contínuo: Múltiplas respostas, todas com `end_of_stream=false`
exceto a última.
##### Esquema do Prompt (`trustgraph-base/trustgraph/schema/services/prompt.py`)
O serviço de prompt envolve a conclusão de texto, portanto, ele segue o mesmo padrão:
**Alterações na Solicitação:**
```python
class PromptRequest(Record):
id = String()
terms = Map(String())
streaming = Boolean() # NEW: Default false
```
**Alterações na Resposta:**
```python
class PromptResponse(Record):
error = Error()
text = String()
object = String()
end_of_stream = Boolean() # NEW: Indicates final message
```
#### Alterações na API Gateway
A API Gateway deve expor capacidades de streaming para clientes HTTP/WebSocket.
**Atualizações da API REST:**
`POST /api/v1/text-completion`: Aceitar o parâmetro `streaming` no corpo da requisição
O comportamento da resposta depende da flag de streaming:
`streaming=false`: Resposta JSON única (comportamento atual)
`streaming=true`: Fluxo de eventos enviados pelo servidor (SSE) ou mensagens WebSocket
**Formato da Resposta (Streaming):**
Cada bloco transmitido segue a mesma estrutura de esquema:
```json
{
"response": "partial text...",
"end_of_stream": false,
"model": "model-name"
}
```
Trecho final:
```json
{
"response": "final text chunk",
"end_of_stream": true,
"in_token": 150,
"out_token": 500,
"model": "model-name"
}
```
#### Alterações na API Python
A API do cliente Python deve suportar tanto o modo de streaming quanto o modo não-streaming,
mantendo a compatibilidade com versões anteriores.
**Atualizações do 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
```
**Atualizações do PromptClient** (`trustgraph-base/trustgraph/base/prompt_client.py`):
Padrão semelhante com o parâmetro `streaming` e a variante de gerador assíncrono.
#### Alterações na Ferramenta de Linha de Comando (CLI)
**tg-invoke-llm** (`trustgraph-cli/trustgraph/cli/invoke_llm.py`):
```
tg-invoke-llm [system] [prompt] [--no-streaming] [-u URL] [-f flow-id]
```
Streaming habilitado por padrão para uma melhor experiência de usuário interativa.
A flag `--no-streaming` desabilita o streaming.
Quando o streaming está habilitado: Envie os tokens para a saída padrão (stdout) à medida que chegam.
Quando o streaming não está habilitado: Aguarde a resposta completa e, em seguida, envie.
**tg-invoke-prompt** (`trustgraph-cli/trustgraph/cli/invoke_prompt.py`):
```
tg-invoke-prompt [template-id] [var=value...] [--no-streaming] [-u URL] [-f flow-id]
```
Mesmo padrão que `tg-invoke-llm`.
#### Alterações na Classe Base do Serviço 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: Prova de Conceito do VertexAI
A Fase 2 implementa o streaming em um único provedor (VertexAI) para validar a
infraestrutura e permitir testes de ponta a ponta.
#### Implementação do VertexAI
**Módulo:** `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
**Alterações:**
1. Substituir `supports_streaming()` para retornar `True`
2. Implementar gerador assíncrono `generate_content_stream()`
3. Lidar com modelos Gemini e Claude (via API Anthropic do VertexAI)
**Streaming do 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 (via VertexAI Anthropic) Streaming:**
```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()
```
#### Testes
Testes unitários para a montagem da resposta em streaming
Testes de integração com o VertexAI (Gemini e Claude)
Testes de ponta a ponta: CLI -> Gateway -> Pulsar -> VertexAI -> de volta
Testes de compatibilidade com versões anteriores: as solicitações não em streaming ainda funcionam
--
### Fase 3: Todos os Provedores de LLM
A Fase 3 estende o suporte a streaming para todos os provedores de LLM no sistema.
#### Status de Implementação do Provedor
Cada provedor deve:
1. **Suporte Completo a Streaming**: Implementar `generate_content_stream()`
2. **Modo de Compatibilidade**: Lidar com a flag `end_of_stream` corretamente
(retornar uma única resposta com `end_of_stream=true`)
| Provedor | Pacote | Suporte a Streaming |
|----------|---------|-------------------|
| OpenAI | trustgraph-flow | Completo (API de streaming nativa) |
| Claude/Anthropic | trustgraph-flow | Completo (API de streaming nativa) |
| Ollama | trustgraph-flow | Completo (API de streaming nativa) |
| Cohere | trustgraph-flow | Completo (API de streaming nativa) |
| Mistral | trustgraph-flow | Completo (API de streaming nativa) |
| Azure OpenAI | trustgraph-flow | Completo (API de streaming nativa) |
| Google AI Studio | trustgraph-flow | Completo (API de streaming nativa) |
| VertexAI | trustgraph-vertexai | Completo (Fase 2) |
| Bedrock | trustgraph-bedrock | Completo (API de streaming nativa) |
| LM Studio | trustgraph-flow | Completo (compatível com OpenAI) |
| LlamaFile | trustgraph-flow | Completo (compatível com OpenAI) |
| vLLM | trustgraph-flow | Completo (compatível com OpenAI) |
| TGI | trustgraph-flow | A ser definido |
| Azure | trustgraph-flow | A ser definido |
#### Padrão de Implementação
Para provedores compatíveis com 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 do Agente
A Fase 4 estende o streaming para a API do Agente. Isso é mais complexo porque a
API do Agente já é inerentemente multi-mensagem (pensamento → ação → observação
→ repetir → resposta final).
#### Esquema Atual do 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()
```
#### Alterações Propostas no Esquema do Agente
**Solicitar Alterações:**
```python
class AgentRequest(Record):
question = String()
state = String()
group = Array(String())
history = Array(AgentStep())
user = String()
streaming = Boolean() # NEW: Default false
```
**Alterações na Resposta:**
O agente produz múltiplos tipos de saída durante seu ciclo de raciocínio:
Pensamentos (raciocínio)
Ações (chamadas de ferramentas)
Observações (resultados das ferramentas)
Resposta (resposta final)
Erros
Como `chunk_type` identifica o tipo de conteúdo que está sendo enviado, os campos separados
`answer`, `error`, `thought` e `observation` podem ser combinados em
um ú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 dos Campos:**
`chunk_type`: Indica o tipo de conteúdo presente no campo `content`
`"thought"`: Raciocínio/pensamento do agente
`"action"`: Ferramenta/ação sendo invocada
`"observation"`: Resultado da execução da ferramenta
`"answer"`: Resposta final à pergunta do usuário
`"error"`: Mensagem de erro
`content`: O conteúdo transmitido, interpretado com base em `chunk_type`
`end_of_message`: Quando `true`, o tipo de bloco atual está completo
Exemplo: Todos os tokens para o pensamento atual foram enviados
Permite que os clientes saibam quando avançar para a próxima etapa
`end_of_dialog`: Quando `true`, toda a interação do agente está completa
Esta é a mensagem final no fluxo
#### Comportamento de Streaming do Agente
Quando `streaming=true`:
1. **Streaming de pensamento:**
Múltiplos blocos com `chunk_type="thought"`, `end_of_message=false`
O bloco final do pensamento tem `end_of_message=true`
2. **Notificação de ação:**
Um único bloco com `chunk_type="action"`, `end_of_message=true`
3. **Observação:**
Bloco(s) com `chunk_type="observation"`, o final tem `end_of_message=true`
4. **Repita** as etapas 1-3 enquanto o agente raciocina
5. **Resposta final:**
`chunk_type="answer"` com a resposta final em `content`
O último bloco tem `end_of_message=true`, `end_of_dialog=true`
**Exemplo de Sequência de Streaming:**
```
{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}
```
Quando `streaming=false`:
Comportamento atual preservado
Resposta única com resposta completa
`end_of_message=true`, `end_of_dialog=true`
#### Gateway e API Python
Gateway: Novo endpoint SSE/WebSocket para streaming de agentes
API Python: Novo método gerador assíncrono `agent_stream()`
--
## Considerações de Segurança
**Nenhuma nova superfície de ataque**: O streaming usa a mesma autenticação/autorização
**Limitação de taxa**: Aplique limites de taxa por token ou por bloco, se necessário
**Gerenciamento de conexão**: Termine corretamente os streams em caso de desconexão do cliente
**Gerenciamento de tempo limite**: As solicitações de streaming precisam de um tratamento de tempo limite adequado
## Considerações de Desempenho
**Memória**: O streaming reduz o uso máximo de memória (sem bufferização completa da resposta)
**Latência**: O tempo para o primeiro token é significativamente reduzido
**Sobrecarga de conexão**: As conexões SSE/WebSocket têm uma sobrecarga de keep-alive
**Throughput do Pulsar**: Múltiplas mensagens pequenas vs. uma única mensagem grande
tradeoff
## Estratégia de Testes
### Testes Unitários
Serialização/desserialização de esquema com novos campos
Compatibilidade com versões anteriores (campos ausentes usam valores padrão)
Lógica de montagem de blocos
### Testes de Integração
Implementação de streaming de cada provedor de LLM
Pontos finais de streaming da API Gateway
Métodos de streaming do cliente Python
### Testes de Ponta a Ponta
Saída de streaming da ferramenta CLI
Fluxo completo: Cliente → Gateway → Pulsar → LLM → de volta
Cargas de trabalho mistas de streaming/não streaming
### Testes de Compatibilidade com Versões Anteriores
Clientes existentes funcionam sem modificação
As solicitações não de streaming se comportam de forma idêntica
## Plano de Migração
### Fase 1: Infraestrutura
Implante as alterações de esquema (compatível com versões anteriores)
Implante as atualizações da API Gateway
Implante as atualizações da API Python
Lance as atualizações da ferramenta CLI
### Fase 2: VertexAI
Implementar a implementação de streaming do VertexAI
Validar com cargas de trabalho de teste
### Fase 3: Todos os Provedores
Implementar as atualizações do provedor de forma incremental
Monitorar para identificar problemas
### Fase 4: API do Agente
Implementar as alterações do esquema do agente
Implementar a implementação de streaming do agente
Atualizar a documentação
## Cronograma
| Fase | Descrição | Dependências |
|-------|-------------|--------------|
| Fase 1 | Infraestrutura | Nenhum |
| Fase 2 | Prova de Conceito do VertexAI | Fase 1 |
| Fase 3 | Todos os Provedores | Fase 2 |
| Fase 4 | API do Agente | Fase 3 |
## Decisões de Design
As seguintes perguntas foram resolvidas durante a especificação:
1. **Contagem de Tokens no Streaming**: As contagens de tokens são diferenças, não totais cumulativos.
Os consumidores podem somá-las, se necessário. Isso corresponde à forma como a maioria dos provedores relata
o uso e simplifica a implementação.
2. **Tratamento de Erros em Streams**: Se ocorrer um erro, o campo `error` é
preenchido e nenhum outro campo é necessário. Um erro é sempre a comunicação final - nenhuma mensagem subsequente é permitida ou esperada após
isso.
um erro. Para fluxos de LLM/Prompt, `end_of_stream=true`. Para fluxos de Agente,
`chunk_type="error"` com `end_of_dialog=true`.
3. **Recuperação Parcial de Respostas**: O protocolo de mensagens (Pulsar) é resiliente,
portanto, a repetição em nível de mensagem não é necessária. Se um cliente perder o controle do fluxo
ou desconectar, ele deve repetir a solicitação completa do zero.
4. **Streaming de Respostas Rápidas**: O streaming é suportado apenas para respostas de texto (`text`).
As respostas estruturadas (`object`) não são suportadas. O serviço de respostas rápidas sabe,
desde o início, se a saída será JSON ou texto, com base no modelo da solicitação. Se
uma solicitação de streaming for feita para uma solicitação de saída JSON, o
serviço deve:
Retornar o JSON completo em uma única resposta com `end_of_stream=true`, ou
Rejeitar a solicitação de streaming com um erro.
## Perguntas Abertas
Nenhum neste momento.
## Referências
Esquema atual do LLM: `trustgraph-base/trustgraph/schema/services/llm.py`
Esquema atual do prompt: `trustgraph-base/trustgraph/schema/services/prompt.py`
Esquema atual do agente: `trustgraph-base/trustgraph/schema/services/agent.py`
Serviço base do LLM: `trustgraph-base/trustgraph/base/llm_service.py`
Provedor VertexAI: `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
API de gateway: `trustgraph-base/trustgraph/api/`
Ferramentas de linha de comando: `trustgraph-cli/trustgraph/cli/`

View file

@ -0,0 +1,621 @@
---
layout: default
title: "Especificação Técnica de Dados Estruturados (Parte 2)"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Dados Estruturados (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.
## Visão Geral
Esta especificação aborda problemas e lacunas identificadas durante a implementação inicial da integração de dados estruturados do TrustGraph, conforme descrito em `structured-data.md`.
## Declarações do Problema
### 1. Inconsistência na Nomenclatura: "Objeto" vs "Linha"
A implementação atual usa a terminologia "objeto" em todo o código (por exemplo, `ExtractedObject`, extração de objetos, incorporações de objetos). Essa nomenclatura é muito genérica e causa confusão:
"Objeto" é um termo sobrecarregado em software (objetos Python, objetos JSON, etc.)
Os dados que estão sendo processados são fundamentalmente tabulares - linhas em tabelas com esquemas definidos
"Linha" descreve com mais precisão o modelo de dados e está alinhado com a terminologia de banco de dados
Essa inconsistência aparece em nomes de módulos, nomes de classes, tipos de mensagens e documentação.
### 2. Limitações de Consulta do Armazenamento de Linhas
A implementação atual do armazenamento de linhas tem limitações significativas de consulta:
**Incompatibilidade com a Linguagem Natural**: As consultas têm dificuldades com variações de dados do mundo real. Por exemplo:
É difícil encontrar uma base de dados de ruas contendo `"CHESTNUT ST"` quando se pergunta sobre `"Chestnut Street"`
Abreviaturas, diferenças de maiúsculas e minúsculas e variações de formatação interrompem as consultas de correspondência exata
Os usuários esperam compreensão semântica, mas o armazenamento fornece correspondência literal
**Problemas de Evolução do Esquema**: Alterar os esquemas causa problemas:
Os dados existentes podem não estar em conformidade com os esquemas atualizados
Alterações na estrutura da tabela podem quebrar consultas e a integridade dos dados
Não há um caminho de migração claro para atualizações de esquema
### 3. Incorporações de Linhas Necessárias
Relacionado ao problema 2, o sistema precisa de incorporações vetoriais para dados de linha para permitir:
Pesquisa semântica em dados estruturados (encontrando "Chestnut Street" quando os dados contêm "CHESTNUT ST")
Correspondência de similaridade para consultas aproximadas
Pesquisa híbrida combinando filtros estruturados com similaridade semântica
Melhor suporte para consultas em linguagem natural
O serviço de incorporação foi especificado, mas não implementado.
### 4. Ingestão de Dados de Linha Incompleta
O pipeline de ingestão de dados estruturados não está totalmente operacional:
Existem prompts de diagnóstico para classificar formatos de entrada (CSV, JSON, etc.)
O serviço de ingestão que usa esses prompts não está integrado ao sistema
Não há um caminho de ponta a ponta para carregar dados pré-estruturados no armazenamento de linhas
## Objetivos
**Flexibilidade de Esquema**: Permitir a evolução do esquema sem quebrar dados existentes ou exigir migrações
**Nomenclatura Consistente**: Padronizar na terminologia "linha" em todo o código
**Consultabilidade Semântica**: Suportar correspondência aproximada/semântica por meio de incorporações de linhas
**Pipeline de Ingestão Completo**: Fornecer um caminho de ponta a ponta para carregar dados estruturados
## Design Técnico
### Esquema de Armazenamento de Linhas Unificado
A implementação anterior criou uma tabela Cassandra separada para cada esquema. Isso causou problemas quando os esquemas evoluíram, pois as alterações na estrutura da tabela exigiram migrações.
O novo design usa uma única tabela unificada para todos os dados de linha:
```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)
)
```
#### Definições das Colunas
| Coluna | Tipo | Descrição |
|--------|------|-------------|
| `collection` | `text` | Identificador de coleta/importação de dados (a partir de metadados) |
| `schema_name` | `text` | Nome do esquema ao qual esta linha está em conformidade |
| `index_name` | `text` | Nome(s) do(s) campo(s) indexado(s), unidos por vírgula para compostos |
| `index_value` | `frozen<list<text>>` | Valor(es) do índice como uma lista |
| `data` | `map<text, text>` | Dados da linha como pares chave-valor |
| `source` | `text` | URI opcional que faz referência a informações de procedência no grafo de conhecimento. Uma string vazia ou NULL indica que não há fonte. |
#### Tratamento de Índices
Cada linha é armazenada várias vezes - uma vez por campo indexado definido no esquema. Os campos de chave primária são tratados como um índice sem um marcador especial, proporcionando flexibilidade futura.
**Exemplo de índice de campo único:**
O esquema define `email` como indexado
`index_name = "email"`
`index_value = ['foo@bar.com']`
**Exemplo de índice composto:**
O esquema define um índice composto em `region` e `status`
`index_name = "region,status"` (nomes dos campos ordenados e unidos por vírgula)
`index_value = ['US', 'active']` (valores na mesma ordem dos nomes dos campos)
**Exemplo de chave primária:**
O esquema define `customer_id` como chave primária
`index_name = "customer_id"`
`index_value = ['CUST001']`
#### Padrões de Consulta
Todas as consultas seguem o mesmo padrão, independentemente de qual índice é usado:
```sql
SELECT * FROM rows
WHERE collection = 'import_2024'
AND schema_name = 'customers'
AND index_name = 'email'
AND index_value = ['foo@bar.com']
```
#### Compensações no Design
**Vantagens:**
Alterações no esquema não exigem alterações na estrutura da tabela
Os dados das linhas são opacos para o Cassandra - adições/remoções de campos são transparentes
Padrão de consulta consistente para todos os métodos de acesso
Sem índices secundários do Cassandra (que podem ser lentos em grande escala)
Tipos nativos do Cassandra em todo o sistema (`map`, `frozen<list>`)
**Compensações:**
Amplificação de escrita: cada inserção de linha = N inserções (uma por campo indexado)
Sobrecarga de armazenamento devido à duplicação de dados das linhas
Informações de tipo armazenadas na configuração do esquema, conversão na camada de aplicação
#### Modelo de Consistência
O design aceita certas simplificações:
1. **Sem atualizações de linha**: O sistema é somente de anexação. Isso elimina as preocupações com a consistência ao atualizar várias cópias da mesma linha.
2. **Tolerância a alterações de esquema**: Quando os esquemas são alterados (por exemplo, índices adicionados/removidos), as linhas existentes mantêm seu índice original. Linhas antigas não serão encontradas por meio de novos índices. Os usuários podem excluir e recriar um esquema para garantir a consistência, se necessário.
### Rastreamento e Exclusão de Partições
#### O Problema
Com a chave de partição `(collection, schema_name, index_name)`, a exclusão eficiente requer o conhecimento de todas as chaves de partição a serem excluídas. Excluir apenas por `collection` ou `collection + schema_name` requer o conhecimento de todos os valores de `index_name` que contêm dados.
#### Tabela de Rastreamento de Partições
Uma tabela de consulta secundária rastreia quais partições existem:
```sql
CREATE TABLE row_partitions (
collection text,
schema_name text,
index_name text,
PRIMARY KEY ((collection), schema_name, index_name)
)
```
Isso permite a descoberta eficiente de partições para operações de exclusão.
#### Comportamento do Escritor de Linhas
O escritor de linhas mantém um cache na memória de pares `(collection, schema_name)` registrados. Ao processar uma linha:
1. Verifique se `(collection, schema_name)` está no cache.
2. Se não estiver no cache (primeira linha para este par):
Consulte a configuração do esquema para obter todos os nomes de índice.
Insira entradas em `row_partitions` para cada `(collection, schema_name, index_name)`.
Adicione o par ao cache.
3. Continue com a escrita dos dados da linha.
O escritor de linhas também monitora eventos de alteração da configuração do esquema. Quando um esquema é alterado, as entradas relevantes do cache são limpas para que a próxima linha acione o re-registro com os nomes de índice atualizados.
Essa abordagem garante:
As escritas na tabela de pesquisa ocorrem apenas uma vez por par `(collection, schema_name)`, e não por linha.
A tabela de pesquisa reflete os índices que estavam ativos quando os dados foram escritos.
As alterações no esquema durante a importação são detectadas corretamente.
#### Operações de Exclusão
**Excluir coleção:**
```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';
```
**Excluir coleção + 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';
```
### Incorporações de Linhas
As incorporações de linhas permitem a correspondência semântica/aproximada em valores indexados, resolvendo o problema de incompatibilidade de linguagem natural (por exemplo, encontrar "CHESTNUT ST" ao pesquisar por "Chestnut Street").
#### Visão Geral do Design
Cada valor indexado é incorporado e armazenado em um armazenamento vetorial (Qdrant). No momento da consulta, a consulta é incorporada, vetores semelhantes são encontrados e os metadados associados são usados para pesquisar as linhas reais no Cassandra.
#### Estrutura da Coleção Qdrant
Uma coleção Qdrant por tupla `(user, collection, schema_name, dimension)`:
**Nome da coleção:** `rows_{user}_{collection}_{schema_name}_{dimension}`
Os nomes são higienizados (caracteres não alfanuméricos substituídos por `_`, em letras minúsculas, os prefixos numéricos recebem o prefixo `r_`)
**Justificativa:** Permite a exclusão limpa de uma instância `(user, collection, schema_name)`, descartando as coleções Qdrant correspondentes; o sufixo de dimensão permite que diferentes modelos de incorporação coexistam.
#### O Que é Incorporado
A representação de texto dos valores do índice:
| Tipo de Índice | Exemplo `index_value` | Texto a Incorporar |
|------------|----------------------|---------------|
| Campo único | `['foo@bar.com']` | `"foo@bar.com"` |
| Composto | `['US', 'active']` | `"US active"` (juntados por espaço) |
#### Estrutura do Ponto
Cada ponto Qdrant contém:
```json
{
"id": "<uuid>",
"vector": [0.1, 0.2, ...],
"payload": {
"index_name": "street_name",
"index_value": ["CHESTNUT ST"],
"text": "CHESTNUT ST"
}
}
```
| Campo de Payload | Descrição |
|---------------|-------------|
| `index_name` | Os campos indexados que esta incorporação representa |
| `index_value` | A lista original de valores (para pesquisa no Cassandra) |
| `text` | O texto que foi incorporado (para depuração/exibição) |
Nota: `user`, `collection` e `schema_name` são implícitos do nome da coleção Qdrant.
#### Fluxo de Consulta
1. O usuário consulta por "Chestnut Street" dentro do usuário U, coleção X, esquema Y
2. Incorpore o texto da consulta
3. Determine o(s) nome(s) da coleção Qdrant que correspondem ao prefixo `rows_U_X_Y_`
4. Pesquise na(s) coleção(ões) Qdrant correspondente(s) pelos vetores mais próximos
5. Obtenha os pontos correspondentes com payloads contendo `index_name` e `index_value`
6. Consulte o Cassandra:
```sql
SELECT * FROM rows
WHERE collection = 'X'
AND schema_name = 'Y'
AND index_name = '<from payload>'
AND index_value = <from payload>
```
7. Retornar linhas correspondentes
#### Opcional: Filtragem por Nome do Índice
As consultas podem opcionalmente filtrar por `index_name` no Qdrant para pesquisar apenas campos específicos:
**"Encontrar qualquer campo que corresponda a 'Chestnut'"** → pesquisa todos os vetores na coleção
**"Encontrar street_name que corresponda a 'Chestnut'"** → filtrar onde `payload.index_name = 'street_name'`
#### Arquitetura
Os embeddings das linhas seguem o **padrão de duas etapas** usado pelo GraphRAG (embeddings do grafo, embeddings do documento):
**Etapa 1: Computação de embeddings** (`trustgraph-flow/trustgraph/embeddings/row_embeddings/`) - Consome `ExtractedObject`, calcula embeddings através do serviço de embeddings, gera `RowEmbeddings`
**Etapa 2: Armazenamento de embeddings** (`trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/`) - Consome `RowEmbeddings`, escreve vetores no Qdrant
O escritor de linhas do Cassandra é um consumidor paralelo separado:
**Escritor de linhas do Cassandra** (`trustgraph-flow/trustgraph/storage/rows/cassandra`) - Consome `ExtractedObject`, escreve linhas no Cassandra
Todos os três serviços consomem do mesmo fluxo, mantendo-os desacoplados. Isso permite:
Escalonamento independente das escritas do Cassandra em relação à geração de embeddings em relação ao armazenamento de vetores
Os serviços de embeddings podem ser desativados se não forem necessários
Falhas em um serviço não afetam os outros
Arquitetura consistente com os pipelines do GraphRAG
#### Caminho de Escrita
**Etapa 1 (processador de embeddings das linhas):** Ao receber um `ExtractedObject`:
1. Consultar o esquema para encontrar campos indexados
2. Para cada campo indexado:
Construir a representação de texto do valor do índice
Calcular o embedding através do serviço de embeddings
3. Gerar uma mensagem `RowEmbeddings` contendo todos os vetores calculados
**Etapa 2 (escritor de embeddings-Qdrant):** Ao receber um `RowEmbeddings`:
1. Para cada embedding na mensagem:
Determinar a coleção do Qdrant a partir de `(user, collection, schema_name, dimension)`
Criar a coleção, se necessário (criação preguiçosa na primeira escrita)
Inserir/atualizar o ponto com o vetor e o payload
#### Tipos de Mensagens
```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]
```
#### Integração de Exclusão
As coleções Qdrant são descobertas por correspondência de prefixo no padrão de nome da coleção:
**Excluir `(user, collection)`:**
1. Listar todas as coleções Qdrant que correspondem ao prefixo `rows_{user}_{collection}_`
2. Excluir cada coleção correspondente
3. Excluir partições de linhas do Cassandra (como documentado acima)
4. Limpar as entradas `row_partitions`
**Excluir `(user, collection, schema_name)`:**
1. Listar todas as coleções Qdrant que correspondem ao prefixo `rows_{user}_{collection}_{schema_name}_`
2. Excluir cada coleção correspondente (lida com múltiplas dimensões)
3. Excluir partições de linhas do Cassandra
4. Limpar `row_partitions`
#### Localizações dos Módulos
| Estágio | Módulo | Ponto de Entrada |
|-------|--------|-------------|
| Estágio 1 | `trustgraph-flow/trustgraph/embeddings/row_embeddings/` | `row-embeddings` |
| Estágio 2 | `trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/` | `row-embeddings-write-qdrant` |
### API de Consulta de Incorporações de Linhas
A consulta de incorporações de linhas é uma **API separada** do serviço de consulta de linhas GraphQL:
| API | Propósito | Backend |
|-----|---------|---------|
| Consulta de Linhas (GraphQL) | Correspondência exata em campos indexados | Cassandra |
| Consulta de Incorporações de Linhas | Correspondência aproximada/semântica | Qdrant |
Essa separação mantém as responsabilidades bem definidas:
O serviço GraphQL se concentra em consultas estruturadas e exatas
A API de incorporações lida com a similaridade semântica
Fluxo de trabalho do usuário: pesquisa aproximada por meio de incorporações para encontrar candidatos e, em seguida, consulta exata para obter os dados completos da linha
#### Esquema de Solicitação/Resposta
```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] = []
```
#### Processador de Consultas
Módulo: `trustgraph-flow/trustgraph/query/row_embeddings/qdrant`
Ponto de entrada: `row-embeddings-query-qdrant`
O processador:
1. Recebe `RowEmbeddingsRequest` com vetores de consulta
2. Encontra a coleção Qdrant apropriada por correspondência de prefixo
3. Procura os vetores mais próximos com filtro opcional `index_name`
4. Retorna `RowEmbeddingsResponse` com informações do índice correspondente
#### Integração com o Gateway de API
O gateway expõe consultas de incorporações de linhas através do padrão padrão de solicitação/resposta:
| Componente | Localização |
|-----------|----------|
| Dispatcher | `trustgraph-flow/trustgraph/gateway/dispatch/row_embeddings_query.py` |
| Registro | Adicione `"row-embeddings"` a `request_response_dispatchers` em `manager.py` |
Nome da interface do fluxo: `row-embeddings`
Definição da interface no blueprint do fluxo:
```json
{
"interfaces": {
"row-embeddings": {
"request": "non-persistent://tg/request/row-embeddings:{id}",
"response": "non-persistent://tg/response/row-embeddings:{id}"
}
}
}
```
#### Suporte ao SDK Python
O SDK fornece métodos para consultas de incorporações de linhas:
```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
```
#### Utilitário de Linha de Comando (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
```
#### Padrão de Uso Típico
A consulta de incorporações de linhas é normalmente usada como parte de um fluxo de pesquisa aproximada para correspondência exata:
```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 padrão de duas etapas permite:
Encontrar "CHESTNUT ST" quando o usuário pesquisa por "Chestnut Street"
Recuperar dados completos da linha com todos os campos
Combinar similaridade semântica com acesso a dados estruturados
### Ingestão de Dados da Linha
Será adiada para uma fase posterior. Será projetada juntamente com outras alterações de ingestão.
## Impacto na Implementação
### Análise do Estado Atual
A implementação existente possui dois componentes principais:
| Componente | Localização | Linhas | Descrição |
|-----------|----------|-------|-------------|
| Serviço de Consulta | `trustgraph-flow/trustgraph/query/objects/cassandra/service.py` | ~740 | Monolítico: geração de esquema GraphQL, análise de filtros, consultas Cassandra, tratamento de solicitações |
| Escritor | `trustgraph-flow/trustgraph/storage/objects/cassandra/write.py` | ~540 | Criação de tabelas por esquema, índices secundários, inserção/exclusão |
**Padrão de Consulta Atual:**
```sql
SELECT * FROM {keyspace}.o_{schema_name}
WHERE collection = 'X' AND email = 'foo@bar.com'
ALLOW FILTERING
```
**Novo Padrão de Consulta:**
```sql
SELECT * FROM {keyspace}.rows
WHERE collection = 'X' AND schema_name = 'customers'
AND index_name = 'email' AND index_value = ['foo@bar.com']
```
### Mudanças Principais
1. **A semântica das consultas é simplificada**: O novo esquema suporta apenas correspondências exatas em `index_value`. Os filtros GraphQL atuais (`gt`, `lt`, `contains`, etc.) ou:
Tornam-se pós-filtragem nos dados retornados (se ainda forem necessários)
São removidos em favor do uso da API de embeddings para correspondências aproximadas
2. **O código GraphQL está fortemente acoplado**: O código `service.py` atual combina a geração de tipos Strawberry, a análise de filtros e as consultas específicas do Cassandra. Adicionar outro backend de armazenamento de linhas duplicaria cerca de 400 linhas de código GraphQL.
### Refatoração Proposta
A refatoração tem duas partes:
#### 1. Separar o Código GraphQL
Extrair componentes GraphQL reutilizáveis para um módulo compartilhado:
```
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
```
Isso permite:
Reutilização em diferentes backends de armazenamento de dados.
Separação mais clara de responsabilidades.
Teste mais fácil da lógica GraphQL de forma independente.
#### 2. Implementar Novo Esquema de Tabela
Refatorar o código específico do Cassandra para usar a tabela unificada:
**Escritor** (`trustgraph-flow/trustgraph/storage/rows/cassandra/`):
Uma única tabela `rows` em vez de tabelas por esquema.
Escrever N cópias por linha (uma por índice).
Registrar na tabela `row_partitions`.
Criação de tabela mais simples (configuração única).
**Serviço de Consulta** (`trustgraph-flow/trustgraph/query/rows/cassandra/`):
Consultar a tabela `rows` unificada.
Usar o módulo GraphQL extraído para geração de esquema.
Tratamento de filtros simplificado (apenas correspondência exata no nível do banco de dados).
### Renomeação de Módulos
Como parte da limpeza de nomes de "objeto" para "linha":
| Atual | Novo |
|---------|-----|
| `storage/objects/cassandra/` | `storage/rows/cassandra/` |
| `query/objects/cassandra/` | `query/rows/cassandra/` |
| `embeddings/object_embeddings/` | `embeddings/row_embeddings/` |
### Novos Módulos
| Módulo | Propósito |
|--------|---------|
| `trustgraph-flow/trustgraph/query/graphql/` | Utilitários GraphQL compartilhados |
| `trustgraph-flow/trustgraph/query/row_embeddings/qdrant/` | API de consulta de incorporações de linhas |
| `trustgraph-flow/trustgraph/embeddings/row_embeddings/` | Cálculo de incorporações de linhas (Etapa 1) |
| `trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/` | Armazenamento de incorporações de linhas (Etapa 2) |
## Referências
[Especificação Técnica de Dados Estruturados](structured-data.md)

View file

@ -0,0 +1,567 @@
---
layout: default
title: "Especificação do Descritor de Dados Estruturados"
parent: "Portuguese (Beta)"
---
# Especificação do Descritor de Dados Estruturados
> **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.
## Visão Geral
O Descritor de Dados Estruturados é uma linguagem de configuração baseada em JSON que descreve como analisar, transformar e importar dados estruturados para o TrustGraph. Ele fornece uma abordagem declarativa para a ingestão de dados, suportando vários formatos de entrada e pipelines de transformação complexos sem a necessidade de código personalizado.
## Conceitos Principais
### 1. Definição de Formato
Descreve o tipo de arquivo de entrada e as opções de análise. Determina qual analisador usar e como interpretar os dados de origem.
### 2. Mapeamentos de Campos
Mapeia caminhos de origem para campos de destino com transformações. Define como os dados fluem das fontes de entrada para os campos do esquema de saída.
### 3. Pipeline de Transformação
Cadeia de transformações de dados que podem ser aplicadas aos valores dos campos, incluindo:
Limpeza de dados (remover espaços em branco, normalização)
Conversão de formato (análise de data, conversão de tipo)
Cálculos (aritméticos, manipulação de strings)
Consultas (tabelas de referência, substituições)
### 4. Regras de Validação
Verificações de qualidade de dados aplicadas para garantir a integridade dos dados:
Validação de tipo
Verificações de intervalo
Correspondência de padrões (regex)
Validação de campos obrigatórios
Lógica de validação personalizada
### 5. Configurações Globais
Configuração que se aplica a todo o processo de importação:
Tabelas de consulta para enriquecimento de dados
Variáveis e constantes globais
Especificações de formato de saída
Políticas de tratamento de erros
## Estratégia de Implementação
A implementação do importador segue este pipeline:
1. **Analisar Configuração** - Carregar e validar o descritor JSON
2. **Inicializar Analisador** - Carregar o analisador apropriado (CSV, XML, JSON, etc.) com base em `format.type`
3. **Aplicar Pré-processamento** - Executar filtros e transformações globais
4. **Processar Registros** - Para cada registro de entrada:
Extrair dados usando caminhos de origem (JSONPath, XPath, nomes de coluna)
Aplicar transformações de nível de campo em sequência
Validar os resultados em relação às regras definidas
Aplicar valores padrão para dados ausentes
5. **Aplicar Pós-processamento** - Executar desduplicação, agregação, etc.
6. **Gerar Saída** - Produzir dados no formato de destino especificado
## Suporte a Expressões de Caminho
Diferentes formatos de entrada usam linguagens de expressão de caminho apropriadas:
**CSV**: Nomes de coluna ou índices (`"column_name"` ou `"[2]"`)
**JSON**: Sintaxe JSONPath (`"$.user.profile.email"`)
**XML**: Expressões XPath (`"//product[@id='123']/price"`)
**Largura Fixa**: Nomes de campo das definições de campo
## Benefícios
**Base de Código Única** - Um único importador lida com vários formatos de entrada
**Fácil de Usar** - Usuários não técnicos podem criar configurações
**Reutilizável** - As configurações podem ser compartilhadas e versionadas
**Flexível** - Transformações complexas sem codificação personalizada
**Robusto** - Validação integrada e tratamento de erros abrangente
**Manutenível** - A abordagem declarativa reduz a complexidade da implementação
## Especificação da Linguagem
O Descritor de Dados Estruturados usa um formato de configuração JSON com a seguinte estrutura de nível 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": { ... }
}
```
### Definição do Formato
Descreve o formato dos dados de entrada e as opções de análise:
```json
{
"format": {
"type": "csv|json|xml|fixed-width|excel|parquet",
"encoding": "utf-8",
"options": {
// Format-specific options
}
}
}
```
#### Opções 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"]
}
}
}
```
#### Opções de Formato JSON
```json
{
"format": {
"type": "json",
"options": {
"root_path": "$.data",
"array_mode": "records|single",
"flatten": false
}
}
}
```
#### Opções de Formato XML
```json
{
"format": {
"type": "xml",
"options": {
"root_element": "//records/record",
"namespaces": {
"ns": "http://example.com/namespace"
}
}
}
}
```
### Configurações Globais
Defina tabelas de consulta, variáveis e configuração 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"
}
}
}
```
### Mapeamentos de Campos
Defina como os dados de origem são mapeados para os campos de destino, com transformações:
```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 Transformação
Funções de transformação disponíveis:
#### Transformações de String
```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"}
```
#### Conversões 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"}
```
#### Operações com Dados
```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"}
```
### Regras de Validação
Verificações de qualidade de dados com tratamento de erros configurável:
#### Validações 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"]}
```
#### Validações 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"
}
```
### Pré-processamento e pós-processamento
Operações globais aplicadas antes/depois do mapeamento 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"}
}
}
]
}
```
### Configuração de Saída
Defina como os dados processados devem ser enviados:
```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"
}
}
}
```
## Exemplo 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
}
}
}
```
## Prompt de LLM para Geração de Descritores
O seguinte prompt pode ser usado para que um LLM analise dados de amostra e gere uma configuração de descritores:
```
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.
```
### Exemplo 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,Nome,Email,Idade,País,Status,Data de Adesão,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
```
### Solicitação para Análise de Dados Existentes Sem Amostra
```
I need you to help me create a Structured Data Descriptor configuration for importing [data type] data.
The source data has these characteristics:
- Format: [CSV/JSON/XML/etc]
- Fields: [list the fields]
- Data quality issues: [describe any known issues]
- Volume: [approximate number of records]
Requirements:
- [List any specific transformation needs]
- [List any validation requirements]
- [List any business rules]
Please generate a Structured Data Descriptor configuration that will:
1. Parse the input format correctly
2. Clean and standardize the data
3. Validate according to the requirements
4. Handle errors gracefully
5. Output in TrustGraph ExtractedObject format
Focus on making the configuration robust and reusable.
```

View file

@ -0,0 +1,147 @@
---
layout: default
title: "Alterações no Esquema Pulsar para Dados Estruturados"
parent: "Portuguese (Beta)"
---
# Alterações no Esquema Pulsar para Dados Estruturados
> **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.
## Visão Geral
Com base na especificação STRUCTURED_DATA.md, este documento propõe as adições e modificações necessárias no esquema Pulsar para suportar as capacidades de dados estruturados no TrustGraph.
## Alterações Necessárias no Esquema
### 1. Melhorias no Esquema Principal
#### Definição de Campo Aprimorada
A classe `Field` existente em `core/primitives.py` precisa de propriedades adicionais:
```python
class Field(Record):
name = String()
type = String() # int, string, long, bool, float, double, timestamp
size = Integer()
primary = Boolean()
description = String()
# NEW FIELDS:
required = Boolean() # Whether field is required
enum_values = Array(String()) # For enum type fields
indexed = Boolean() # Whether field should be indexed
```
### 2. Novos Esquemas de Conhecimento
#### 2.1 Submissão de Dados Estruturados
Novo arquivo: `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() # Reference to schema in config
data = Bytes() # Raw data to ingest
options = Map(String()) # Format-specific options
```
### 3. Novos Esquemas de Serviço
#### 3.1 Serviço de Consulta Estruturada a partir de Processamento de Linguagem Natural
Novo arquivo: `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()) # Optional context for query generation
class NLPToStructuredQueryResponse(Record):
error = Error()
graphql_query = String() # Generated GraphQL query
variables = Map(String()) # GraphQL variables if any
detected_schemas = Array(String()) # Which schemas the query targets
confidence = Double()
```
#### 3.2 Serviço de Consulta Estruturada
Novo arquivo: `services/structured_query.py`
```python
from pulsar.schema import Record, String, Map, Array
from ..core.primitives import Error
class StructuredQueryRequest(Record):
query = String() # GraphQL query
variables = Map(String()) # GraphQL variables
operation_name = String() # Optional operation name for multi-operation documents
class StructuredQueryResponse(Record):
error = Error()
data = String() # JSON-encoded GraphQL response data
errors = Array(String()) # GraphQL errors if any
```
#### 2.2 Saída da Extração de Objetos
Novo arquivo: `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() # Which schema this object belongs to
values = Map(String()) # Field name -> value
confidence = Double()
source_span = String() # Text span where object was found
```
### 4. Esquemas de Conhecimento Aprimorados
#### 4.1 Aprimoramento de Incorporações de Objetos
Atualizar `knowledge/embeddings.py` para oferecer melhor suporte a incorporações de objetos estruturadas:
```python
class StructuredObjectEmbedding(Record):
metadata = Metadata()
vectors = Array(Array(Double()))
schema_name = String()
object_id = String() # Primary key value
field_embeddings = Map(Array(Double())) # Per-field embeddings
```
## Pontos de Integração
### Integração de Fluxo
Os esquemas serão usados por novos módulos de fluxo:
`trustgraph-flow/trustgraph/decoding/structured` - Usa StructuredDataSubmission
`trustgraph-flow/trustgraph/query/nlp_query/cassandra` - Usa esquemas de consulta NLP
`trustgraph-flow/trustgraph/query/objects/cassandra` - Usa esquemas de consulta estruturados
`trustgraph-flow/trustgraph/extract/object/row/` - Consome Chunk, produz ExtractedObject
`trustgraph-flow/trustgraph/storage/objects/cassandra` - Usa o esquema Rows
`trustgraph-flow/trustgraph/embeddings/object_embeddings/qdrant` - Usa esquemas de incorporação de objetos
## Notas de Implementação
1. **Versionamento de Esquemas**: Considere adicionar um campo `version` em RowSchema para suporte futuro de migração.
2. **Sistema de Tipos**: O `Field.type` deve suportar todos os tipos nativos do Cassandra.
3. **Operações em Lote**: A maioria dos serviços deve suportar operações individuais e em lote.
4. **Tratamento de Erros**: Relatório de erros consistente em todos os novos serviços.
5. **Compatibilidade com Versões Anteriores**: Os esquemas existentes permanecem inalterados, exceto por pequenos aprimoramentos de campos.
## Próximos Passos
1. Implementar os arquivos de esquema na nova estrutura.
2. Atualizar os serviços existentes para reconhecer os novos tipos de esquema.
3. Implementar módulos de fluxo que usem esses esquemas.
4. Adicionar endpoints de gateway/rev-gateway para novos serviços.
5. Criar testes unitários para validação de esquema.

View file

@ -0,0 +1,260 @@
---
layout: default
title: "Especificação Técnica de Dados Estruturados"
parent: "Portuguese (Beta)"
---
# Especificação Técnica de Dados Estruturados
> **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.
## Visão Geral
Esta especificação descreve a integração do TrustGraph com fluxos de dados estruturados, permitindo que o sistema trabalhe com dados que podem ser representados como linhas em tabelas ou objetos em armazenamentos de objetos. A integração suporta quatro casos de uso primários:
1. **Extração de Não Estruturado para Estruturado**: Ler fontes de dados não estruturados, identificar e extrair estruturas de objetos e armazená-las em um formato tabular.
2. **Ingestão de Dados Estruturados**: Carregar dados que já estão em formatos estruturados diretamente no armazenamento estruturado, juntamente com os dados extraídos.
3. **Consulta em Linguagem Natural**: Converter perguntas em linguagem natural em consultas estruturadas para extrair dados correspondentes do armazenamento.
4. **Consulta Estruturada Direta**: Executar consultas estruturadas diretamente contra o armazenamento de dados para recuperação precisa de dados.
## Objetivos
**Acesso Unificado a Dados**: Fornecer uma única interface para acessar dados estruturados e não estruturados dentro do TrustGraph.
**Integração Perfeita**: Permitir interoperabilidade perfeita entre a representação de conhecimento baseada em grafo do TrustGraph e formatos de dados estruturados tradicionais.
**Extração Flexível**: Suportar a extração automática de dados estruturados de várias fontes não estruturadas (documentos, texto, etc.).
**Versatilidade de Consulta**: Permitir que os usuários consultem dados usando linguagem natural e linguagens de consulta estruturadas.
**Consistência de Dados**: Manter a integridade e a consistência dos dados em diferentes representações de dados.
**Otimização de Desempenho**: Garantir o armazenamento e a recuperação eficientes de dados estruturados em grande escala.
**Flexibilidade de Esquema**: Suportar abordagens de esquema-na-escrita e esquema-na-leitura para acomodar diversas fontes de dados.
**Compatibilidade com Versões Anteriores**: Preservar a funcionalidade existente do TrustGraph, adicionando recursos de dados estruturados.
## Contexto
Atualmente, o TrustGraph se destaca no processamento de dados não estruturados e na construção de grafos de conhecimento a partir de diversas fontes. No entanto, muitos casos de uso empresariais envolvem dados que são inerentemente estruturados - registros de clientes, logs de transações, bancos de dados de inventário e outros conjuntos de dados tabulares. Esses conjuntos de dados estruturados geralmente precisam ser analisados juntamente com conteúdo não estruturado para fornecer insights abrangentes.
As limitações atuais incluem:
Ausência de suporte nativo para ingestão de formatos de dados pré-estruturados (CSV, arrays JSON, exportações de banco de dados).
Impossibilidade de preservar a estrutura inerente ao extrair dados tabulares de documentos.
Falta de mecanismos de consulta eficientes para padrões de dados estruturados.
Falta de uma ponte entre consultas semelhantes a SQL e as consultas de grafo do TrustGraph.
Esta especificação aborda essas lacunas, introduzindo uma camada de dados estruturados que complementa as capacidades existentes do TrustGraph. Ao suportar dados estruturados nativamente, o TrustGraph pode:
Servir como uma plataforma unificada para análise de dados estruturados e não estruturados.
Permitir consultas híbridas que abrangem relacionamentos de grafo e dados tabulares.
Fornecer interfaces familiares para usuários acostumados a trabalhar com dados estruturados.
Desbloquear novos casos de uso em integração de dados e inteligência de negócios.
## Design Técnico
### Arquitetura
A integração de dados estruturados requer os seguintes componentes técnicos:
1. **Serviço de Conversão de Linguagem Natural para Consulta Estruturada**
Converte perguntas em linguagem natural em consultas estruturadas.
Suporta vários alvos de linguagem de consulta (inicialmente sintaxe semelhante a SQL).
Integra-se com as capacidades existentes de NLP do TrustGraph.
Módulo: trustgraph-flow/trustgraph/query/nlp_query/cassandra
2. **Suporte para Esquema de Configuração****[COMPLETO]**
Sistema de configuração estendido para armazenar esquemas de dados estruturados.
Suporte para definir estruturas de tabela, tipos de campo e relacionamentos.
Versionamento de esquema e capacidades de migração.
3. **Módulo de Extração de Objetos****[COMPLETO]**
Integração aprimorada do fluxo de extração de conhecimento.
Identifica e extrai objetos estruturados de fontes não estruturadas.
Mantém a rastreabilidade e as pontuações de confiança.
Registra um manipulador de configuração (exemplo: trustgraph-flow/trustgraph/prompt/template/service.py) para receber dados de configuração e decodificar informações de esquema.
Recebe objetos e os decodifica em objetos ExtractedObject para entrega na fila Pulsar.
OBS: Existe código existente em `trustgraph-flow/trustgraph/extract/object/row/`. Esta foi uma tentativa anterior e precisará ser refatorada significativamente, pois não está em conformidade com as APIs atuais. Use-o se for útil, comece do zero se não for.
Requer uma interface de linha de comando: `kg-extract-objects`
Módulo: trustgraph-flow/trustgraph/extract/kg/objects/
4. **Módulo de Escrita de Armazenamento Estruturado****[COMPLETO]**
Recebe objetos no formato ExtractedObject de filas Pulsar.
Implementação inicial direcionada ao Apache Cassandra como o armazenamento de dados estruturados.
Lida com a criação dinâmica de tabelas com base nos esquemas encontrados.
Gerencia o mapeamento de esquema para tabela Cassandra e a transformação de dados.
Fornece operações de gravação em lote e em streaming para otimização de desempenho.
Sem saídas Pulsar - este é um serviço terminal no fluxo de dados.
**Manipulação de Esquema**:
Monitora mensagens ExtractedObject recebidas para referências de esquema.
Quando um novo esquema é encontrado pela primeira vez, cria automaticamente a tabela Cassandra correspondente.
Mantém um cache de esquemas conhecidos para evitar tentativas redundantes de criação de tabela.
Deve considerar se deve receber definições de esquema diretamente ou confiar em nomes de esquema em mensagens ExtractedObject.
**Mapeamento de Tabela Cassandra**:
O keyspace é nomeado a partir do campo `user` do Metadata do ExtractedObject.
A tabela é nomeada a partir do campo `schema_name` do ExtractedObject.
A coleção do Metadata se torna parte da chave de partição para garantir:
Distribuição natural dos dados em todos os nós do Cassandra.
Consultas eficientes dentro de uma coleção específica.
Isolamento lógico entre diferentes importações/fontes de dados.
Estrutura da chave primária: `PRIMARY KEY ((collection, <schema_primary_key_fields>), <clustering_keys>)`.
A coleção é sempre o primeiro componente da chave de partição.
Os campos da chave primária definidos no esquema seguem como parte da chave de partição composta.
Isso requer que as consultas especifiquem a coleção, garantindo um desempenho previsível.
As definições de campos são mapeadas para colunas do Cassandra com conversões de tipo:
`string``text`.
`integer``int` ou `bigint` com base na dica de tamanho.
`float``float` ou `double` com base nas necessidades de precisão.
`boolean``boolean`.
`timestamp``timestamp`.
`enum``text` com validação no nível da aplicação.
Campos indexados criam índices secundários do Cassandra (excluindo campos já na chave primária).
Campos obrigatórios são aplicados no nível da aplicação (o Cassandra não suporta NOT NULL).
**Armazenamento de Objetos**:
Extrai valores do mapa ExtractedObject.values.
Realiza conversão de tipo e validação antes da inserção.
Lida com campos opcionais ausentes de forma elegante.
Mantém metadados sobre a origem do objeto (documento de origem, pontuações de confiança).
Suporta escritas idempotentes para lidar com cenários de repetição de mensagens.
**Observações de Implementação**:
O código existente em `trustgraph-flow/trustgraph/storage/objects/cassandra/` está desatualizado e não está em conformidade com as APIs atuais.
Deve referenciar `trustgraph-flow/trustgraph/storage/triples/cassandra` como um exemplo de um processador de armazenamento funcional.
É necessário avaliar o código existente para identificar quaisquer componentes reutilizáveis antes de decidir refatorar ou reescrever.
Módulo: trustgraph-flow/trustgraph/storage/objects/cassandra
5. **Serviço de Consulta Estruturada****[COMPLETO]**
Aceita consultas estruturadas em formatos definidos.
Executa consultas no armazenamento estruturado.
Retorna objetos que correspondem aos critérios da consulta.
Suporta paginação e filtragem de resultados.
Módulo: trustgraph-flow/trustgraph/query/objects/cassandra
6. **Integração com Ferramenta de Agente**
Nova classe de ferramenta para frameworks de agente.
Permite que agentes consultem armazenamentos de dados estruturados.
Fornece interfaces de consulta em linguagem natural e estruturada.
Integra-se com os processos de tomada de decisão existentes dos agentes.
7. **Serviço de Ingestão de Dados Estruturados**
Aceita dados estruturados em vários formatos (JSON, CSV, XML).
Analisa e valida os dados recebidos de acordo com os esquemas definidos.
Converte os dados em fluxos de objetos normalizados.
Emite objetos para as filas de mensagens apropriadas para processamento.
Suporta uploads em lote e ingestão em streaming.
Módulo: trustgraph-flow/trustgraph/decoding/structured
8. **Serviço de Incorporação de Objetos**
Gera incorporações vetoriais para objetos estruturados.
Permite a pesquisa semântica em dados estruturados.
Suporta pesquisa híbrida combinando consultas estruturadas com similaridade semântica.
Integra-se com armazenamentos de vetores existentes.
Módulo: trustgraph-flow/trustgraph/embeddings/object_embeddings/qdrant
### Modelos de Dados
#### Mecanismo de Armazenamento de Esquemas
Os esquemas são armazenados no sistema de configuração do TrustGraph usando a seguinte estrutura:
**Type**: `schema` (valor fixo para todos os esquemas de dados estruturados)
**Key**: O nome/identificador exclusivo do esquema (por exemplo, `customer_records`, `transaction_log`)
**Value**: Definição de esquema JSON contendo a estrutura
Exemplo de entrada de configuração:
```
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"]
}
```
Esta abordagem permite:
Definição dinâmica de esquema sem alterações de código
Atualizações e versionamento de esquema fáceis
Integração consistente com o gerenciamento de configuração existente do TrustGraph
Suporte para vários esquemas dentro de um único ambiente de implantação
### APIs
Novas APIs:
Esquemas Pulsar para os tipos acima
Interfaces Pulsar em novos fluxos
É necessário um meio de especificar os tipos de esquema nos fluxos para que os fluxos saibam quais
tipos de esquema carregar
APIs adicionadas ao gateway e rev-gateway
APIs modificadas:
Pontos de extremidade de extração de conhecimento - Adicionar opção de saída de objeto estruturado
Pontos de extremidade do agente - Adicionar suporte para ferramentas de dados estruturados
### Detalhes da Implementação
Seguindo as convenções existentes - estes são apenas novos módulos de processamento.
Tudo está nos pacotes trustgraph-flow, exceto os itens de esquema
em trustgraph-base.
É necessário algum trabalho de interface do usuário no Workbench para poder demonstrar / testar
essa funcionalidade.
## Considerações de Segurança
Nenhuma consideração adicional.
## Considerações de Desempenho
Algumas perguntas sobre o uso de consultas e índices do Cassandra para que as consultas
não diminuam a velocidade.
## Estratégia de Teste
Use a estratégia de teste existente, serão criados testes unitários, de contrato e de integração.
## Plano de Migração
Nenhum.
## Cronograma
Não especificado.
## Perguntas Abertas
Isso pode ser adaptado para funcionar com outros tipos de armazenamento? Estamos buscando usar
interfaces que tornem os módulos que funcionam com um armazenamento aplicáveis a
outros armazenamentos.
## Referências
n/a.

View file

@ -0,0 +1,281 @@
---
layout: default
title: "Especificação Técnica do Serviço de Diagnóstico de Dados Estruturados"
parent: "Portuguese (Beta)"
---
# Especificação Técnica do Serviço de Diagnóstico de Dados Estruturados
> **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.
## Visão Geral
Esta especificação descreve um novo serviço invocável para diagnosticar e analisar dados estruturados dentro do TrustGraph. O serviço extrai a funcionalidade da ferramenta de linha de comando existente `tg-load-structured-data` e a expõe como um serviço de solicitação/resposta, permitindo o acesso programático às capacidades de detecção de tipo de dados e geração de descritores.
O serviço suporta três operações principais:
1. **Detecção de Tipo de Dados**: Analisa uma amostra de dados para determinar seu formato (CSV, JSON ou XML)
2. **Geração de Descritor**: Gera um descritor de dados estruturados do TrustGraph para uma determinada amostra de dados e tipo
3. **Diagnóstico Combinado**: Realiza a detecção de tipo e a geração de descritor em sequência
## Objetivos
**Modularização da Análise de Dados**: Extrai a lógica de diagnóstico de dados da CLI para componentes de serviço reutilizáveis
**Habilitar Acesso Programático**: Fornecer acesso baseado em API às capacidades de análise de dados
**Suportar Múltiplos Formatos de Dados**: Lidar com formatos de dados CSV, JSON e XML de forma consistente
**Gerar Descritores Precisos**: Produzir descritores de dados estruturados que mapeiem com precisão os dados de origem para os esquemas do TrustGraph
**Manter a Compatibilidade com Versões Anteriores**: Garantir que a funcionalidade existente da CLI continue a funcionar
**Habilitar a Composição de Serviços**: Permitir que outros serviços aproveitem as capacidades de diagnóstico de dados
**Melhorar a Testabilidade**: Separar a lógica de negócios da interface da CLI para melhor teste
**Suportar a Análise em Streaming**: Permitir a análise de amostras de dados sem carregar arquivos inteiros
## Contexto
Atualmente, o comando `tg-load-structured-data` fornece funcionalidade abrangente para analisar dados estruturados e gerar descritores. No entanto, essa funcionalidade está fortemente acoplada à interface da CLI, limitando sua reutilização.
As limitações atuais incluem:
Lógica de diagnóstico de dados incorporada no código da CLI
Sem acesso programático à detecção de tipo e à geração de descritores
Difícil integrar as capacidades de diagnóstico em outros serviços
Capacidade limitada de compor fluxos de trabalho de análise de dados
Esta especificação aborda essas lacunas, criando um serviço dedicado para o diagnóstico de dados estruturados. Ao expor essas capacidades como um serviço, o TrustGraph pode:
Permitir que outros serviços analisem dados programaticamente
Suportar pipelines de processamento de dados mais complexos
Facilitar a integração com sistemas externos
Melhorar a manutenção por meio da separação de responsabilidades
## Design Técnico
### Arquitetura
O serviço de diagnóstico de dados estruturados requer os seguintes componentes técnicos:
1. **Processador do Serviço de Diagnóstico**
Lida com solicitações de diagnóstico recebidas
Orquestra a detecção de tipo e a geração de descritores
Retorna respostas estruturadas com os resultados do diagnóstico
Módulo: `trustgraph-flow/trustgraph/diagnosis/structured_data/service.py`
2. **Detector de Tipo de Dados**
Usa a detecção algorítmica para identificar o formato de dados (CSV, JSON, XML)
Analisa a estrutura de dados, delimitadores e padrões de sintaxe
Retorna o formato detectado e as pontuações de confiança
Módulo: `trustgraph-flow/trustgraph/diagnosis/structured_data/type_detector.py`
3. **Gerador de Descritores**
Usa o serviço de prompt para gerar descritores
Invoca prompts específicos do formato (diagnosticar-csv, diagnosticar-json, diagnosticar-xml)
Mapeia campos de dados para campos de esquema do TrustGraph por meio de respostas de prompt
Módulo: `trustgraph-flow/trustgraph/diagnosis/structured_data/descriptor_generator.py`
### Modelos de Dados
#### StructuredDataDiagnosisRequest
Mensagem de solicitação para operações de diagnóstico de dados estruturados:
```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)
```
#### StructuredDataDiagnosisResponse
Mensagem de resposta contendo os resultados do 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)
```
#### Estrutura do Descritor
O descritor gerado segue o formato existente de descritor de dados estruturados:
```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
}
}
}
```
### Interface de Serviço
O serviço exporá as seguintes operações através do padrão de solicitação/resposta:
1. **Operação de Detecção de Tipo**
Entrada: Amostra de dados
Processamento: Analisar a estrutura de dados usando detecção algorítmica
Saída: Tipo detectado com pontuação de confiança
2. **Operação de Geração de Descritor**
Entrada: Amostra de dados, tipo, nome do esquema de destino
Processamento:
Chamar o serviço de prompt com o ID de prompt específico do formato (diagnosticar-csv, diagnosticar-json ou diagnosticar-xml)
Passar a amostra de dados e os esquemas disponíveis para o prompt
Receber o descritor gerado da resposta do prompt
Saída: Descritor de dados estruturados
3. **Operação de Diagnóstico Combinado**
Entrada: Amostra de dados, nome de esquema opcional
Processamento:
Usar a detecção algorítmica para identificar o formato primeiro
Selecionar o prompt específico do formato apropriado com base no tipo detectado
Chamar o serviço de prompt para gerar o descritor
Saída: Tanto o tipo detectado quanto o descritor
### Detalhes da Implementação
O serviço seguirá as convenções do serviço TrustGraph:
1. **Registro de Serviço**
Registrar como tipo de serviço `structured-diag`
Usar tópicos padrão de solicitação/resposta
Implementar a classe base FlowProcessor
Registrar PromptClientSpec para a interação com o serviço de prompt
2. **Gerenciamento de Configuração**
Acessar as configurações do esquema através do serviço de configuração
Armazenar em cache os esquemas para desempenho
Lidar com atualizações de configuração dinamicamente
3. **Integração de Prompt**
Usar a infraestrutura existente do serviço de prompt
Chamar o serviço de prompt com IDs de prompt específicos do formato:
`diagnose-csv`: Para análise de dados CSV
`diagnose-json`: Para análise de dados JSON
`diagnose-xml`: Para análise de dados XML
Os prompts são configurados na configuração do prompt, e não codificados no serviço
Passar esquemas e amostras de dados como variáveis de prompt
Analisar as respostas do prompt para extrair os descritores
4. **Tratamento de Erros**
Validar as amostras de dados de entrada
Fornecer mensagens de erro descritivas
Lidar com dados malformados de forma elegante
Lidar com falhas no serviço de prompt
5. **Amostragem de Dados**
Processar tamanhos de amostra configuráveis
Lidar com registros incompletos de forma apropriada
Manter a consistência da amostragem
### Integração de API
O serviço será integrado com as APIs TrustGraph existentes:
Componentes Modificados:
`tg-load-structured-data` CLI - Refatorado para usar o novo serviço para operações de diagnóstico
Flow API - Estendido para suportar solicitações de diagnóstico de dados estruturados
Novos Pontos de Extremidade do Serviço:
`/api/v1/flow/{flow}/diagnose/structured-data` - Endpoint WebSocket para solicitações de diagnóstico
`/api/v1/diagnose/structured-data` - Endpoint REST para diagnóstico síncrono
### Fluxo de Mensagens
```
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)
```
## Considerações de Segurança
Validação de entrada para prevenir ataques de injeção
Limites de tamanho em amostras de dados para prevenir ataques de negação de serviço (DoS)
Sanitização de descritores gerados
Controle de acesso através da autenticação TrustGraph existente
## Considerações de Desempenho
Cache de definições de esquema para reduzir o número de chamadas ao serviço de configuração
Limitar o tamanho das amostras para manter um desempenho responsivo
Usar processamento em fluxo para grandes amostras de dados
Implementar mecanismos de timeout para análises de longa duração
## Estratégia de Testes
1. **Testes Unitários**
Detecção de tipo para vários formatos de dados
Precisão da geração de descritores
Cenários de tratamento de erros
2. **Testes de Integração**
Fluxo de solicitação/resposta do serviço
Recuperação e cache de esquema
Integração com a interface de linha de comando (CLI)
3. **Testes de Desempenho**
Processamento de grandes amostras
Tratamento de solicitações concorrentes
Uso de memória sob carga
## Plano de Migração
1. **Fase 1**: Implementar o serviço com a funcionalidade principal
2. **Fase 2**: Refatorar a CLI para usar o serviço (manter a compatibilidade com versões anteriores)
3. **Fase 3**: Adicionar endpoints de API REST
4. **Fase 4**: Descontinuar a lógica da CLI incorporada (com aviso prévio)
## Cronograma
Semana 1-2: Implementar o serviço principal e a detecção de tipo
Semana 3-4: Adicionar geração de descritores e integração
Semana 5: Testes e documentação
Semana 6: Refatoração da CLI e migração
## Perguntas Abertas
O serviço deve suportar formatos de dados adicionais (por exemplo, Parquet, Avro)?
Qual deve ser o tamanho máximo da amostra para análise?
Os resultados do diagnóstico devem ser armazenados em cache para solicitações repetidas?
Como o serviço deve lidar com cenários de vários esquemas?
Os IDs de prompt devem ser parâmetros configuráveis para o serviço?
## Referências
[Especificação do Descritor de Dados Estruturados](structured-data-descriptor.md)
[Documentação de Carregamento de Dados Estruturados](structured-data.md)
`tg-load-structured-data` implementação: `trustgraph-cli/trustgraph/cli/load_structured_data.py`

View file

@ -0,0 +1,499 @@
---
layout: default
title: "TrustGraph Tool Group System"
parent: "Portuguese (Beta)"
---
# TrustGraph Tool Group System
> **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.
## Especificação Técnica v1.0
### Resumo Executivo
Esta especificação define um sistema de agrupamento de ferramentas para agentes TrustGraph que permite um controle preciso sobre quais ferramentas estão disponíveis para solicitações específicas. O sistema introduz filtragem de ferramentas baseada em grupos por meio de configuração e especificação no nível da solicitação, permitindo melhores limites de segurança, gerenciamento de recursos e particionamento funcional das capacidades do agente.
### 1. Visão Geral
#### 1.1 Declaração do Problema
Atualmente, os agentes TrustGraph têm acesso a todas as ferramentas configuradas, independentemente do contexto da solicitação ou dos requisitos de segurança. Isso cria vários desafios:
**Risco de Segurança**: Ferramentas sensíveis (por exemplo, modificação de dados) estão disponíveis mesmo para consultas somente leitura.
**Desperdício de Recursos**: Ferramentas complexas são carregadas mesmo quando consultas simples não as exigem.
**Confusão Funcional**: Os agentes podem selecionar ferramentas inadequadas quando alternativas mais simples existem.
**Isolamento Multi-inquilino**: Diferentes grupos de usuários precisam de acesso a conjuntos de ferramentas diferentes.
#### 1.2 Visão Geral da Solução
O sistema de agrupamento de ferramentas introduz:
1. **Classificação por Grupo**: As ferramentas são marcadas com associações de grupo durante a configuração.
2. **Filtragem no Nível da Solicitação**: AgentRequest especifica quais grupos de ferramentas são permitidos.
3. **Aplicação em Tempo de Execução**: Os agentes têm acesso apenas a ferramentas que correspondem aos grupos solicitados.
4. **Agrupamento Flexível**: As ferramentas podem pertencer a vários grupos para cenários complexos.
### 2. Alterações no Esquema
#### 2.1 Aprimoramento do Esquema de Configuração da Ferramenta
A configuração existente da ferramenta é aprimorada com um campo `group`:
**Antes:**
```json
{
"name": "knowledge-query",
"type": "knowledge-query",
"description": "Query the knowledge graph"
}
```
**Depois:**
```json
{
"name": "knowledge-query",
"type": "knowledge-query",
"description": "Query the knowledge graph",
"group": ["read-only", "knowledge", "basic"]
}
```
**Especificação do Campo do Grupo:**
`group`: Array(String) - Lista de grupos aos quais esta ferramenta pertence
**Opcional**: Ferramentas sem campo de grupo pertencem ao grupo "padrão"
**Múltipla associação**: As ferramentas podem pertencer a vários grupos
**Sensível a maiúsculas e minúsculas**: Os nomes dos grupos são correspondências exatas de strings
#### 2.1.2 Melhoria da Transição de Estado da Ferramenta
As ferramentas podem, opcionalmente, especificar transições de estado e disponibilidade baseada no 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"]
}
```
**Especificação do Campo de Estado:**
`state`: String - **Opcional** - Estado para o qual transitar após a execução bem-sucedida da ferramenta
`available_in_states`: Array(String) - **Opcional** - Estados nos quais esta ferramenta está disponível
**Comportamento padrão**: Ferramentas sem `available_in_states` estão disponíveis em todos os estados
**Transição de estado**: Ocorre apenas após a execução bem-sucedida da ferramenta
#### 2.2 Melhoria do Esquema AgentRequest
O esquema `AgentRequest` em `trustgraph-base/trustgraph/schema/services/agent.py` é aprimorado:
**AgentRequest Atual:**
`question`: String - Consulta do usuário
`plan`: String - Plano de execução (pode ser removido)
`state`: String - Estado do agente
`history`: Array(AgentStep) - Histórico de execução
**AgentRequest Aprimorado:**
`question`: String - Consulta do usuário
`state`: String - Estado de execução do agente (agora usado ativamente para filtragem de ferramentas)
`history`: Array(AgentStep) - Histórico de execução
`group`: Array(String) - **NOVO** - Grupos de ferramentas permitidos para este pedido
**Alterações no Esquema:**
**Removido**: O campo `plan` não é mais necessário e pode ser removido (originalmente destinado à especificação de ferramentas)
**Adicionado**: O campo `group` para especificação de grupos de ferramentas
**Aprimorado**: O campo `state` agora controla a disponibilidade de ferramentas durante a execução
**Comportamentos dos Campos:**
**Grupo de Campo:**
**Opcional**: Se não especificado, o padrão é ["default"]
**Interseção**: Apenas as ferramentas que correspondem a pelo menos um grupo especificado estão disponíveis
**Array vazio**: Nenhuma ferramenta disponível (o agente só pode usar o raciocínio interno)
**Curinga**: O grupo especial "*" concede acesso a todas as ferramentas
**Campo de Estado:**
**Opcional**: Se não especificado, o padrão é "undefined"
**Filtragem baseada em estado**: Apenas as ferramentas disponíveis no estado atual são elegíveis
**Estado padrão**: O estado "undefined" permite todas as ferramentas (sujeito à filtragem de grupos)
**Transições de estado**: As ferramentas podem alterar o estado após a execução bem-sucedida
### 3. Exemplos de Grupos Personalizados
As organizações podem definir grupos específicos do domínio:
```json
{
"financial-tools": ["stock-query", "portfolio-analysis"],
"medical-tools": ["diagnosis-assist", "drug-interaction"],
"legal-tools": ["contract-analysis", "case-search"]
}
```
### 4. Detalhes de Implementação
#### 4.1 Carregamento e Filtragem de Ferramentas
**Fase de Configuração:**
1. Todas as ferramentas são carregadas da configuração com suas atribuições de grupo.
2. Ferramentas sem grupos explícitos são atribuídas ao grupo "padrão".
3. A associação a grupos é validada e armazenada no registro de ferramentas.
**Fase de Processamento de Solicitações:**
1. Uma solicitação do agente chega com uma especificação de grupo opcional.
2. O agente filtra as ferramentas disponíveis com base na interseção de grupos.
3. Apenas as ferramentas correspondentes são passadas para o contexto de execução do agente.
4. O agente opera com o conjunto de ferramentas filtrado durante todo o ciclo de vida da solicitação.
#### 4.2 Lógica de Filtragem de Ferramentas
**Filtragem Combinada de Grupo e 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 Transição 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 Pontos de Integração do Agente
**Agente ReAct:**
O filtro de ferramentas ocorre em agent_manager.py durante a criação do registro de ferramentas.
A lista de ferramentas disponíveis é filtrada por grupo e estado antes da geração do plano.
As transições de estado atualizam o campo AgentRequest.state após a execução bem-sucedida da ferramenta.
A próxima iteração usa o estado atualizado para o filtro de ferramentas.
**Agente Baseado em Confiança:**
O filtro de ferramentas ocorre em planner.py durante a geração do plano.
A validação de ExecutionStep garante que apenas as ferramentas elegíveis por grupo e estado sejam usadas.
O controlador de fluxo impõe a disponibilidade da ferramenta em tempo de execução.
As transições de estado são gerenciadas pelo Controlador de Fluxo entre os passos.
### 5. Exemplos de Configuração
#### 5.1 Configuração de Ferramentas com Grupos e 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 Exemplos de Solicitações com Fluxos de Trabalho de Estado
**Solicitação de Pesquisa Inicial:**
```json
{
"question": "What entities are connected to Company X?",
"group": ["read-only", "knowledge"],
"state": "undefined"
}
```
*Ferramentas disponíveis: knowledge-query, text-completion*
*Após knowledge-query: estado → "análise"*
**Fase de Análise:**
```json
{
"question": "Continue analysis based on previous results",
"group": ["advanced", "compute", "write"],
"state": "analysis"
}
```
*Ferramentas disponíveis: complex-analysis, graph-update, reset-workflow*
*Após complex-analysis: estado → "results"*
**Fase de Resultados:**
```json
{
"question": "What should I do with these results?",
"group": ["admin"],
"state": "results"
}
```
*Ferramentas disponíveis: apenas reset-workflow*
*Após reset-workflow: estado → "indefinido"*
**Exemplo de Fluxo de Trabalho - Fluxo Completo:**
1. **Início (indefinido)**: Use knowledge-query → transições para "análise"
2. **Estado de análise**: Use complex-analysis → transições para "resultados"
3. **Estado de resultados**: Use reset-workflow → transições de volta para "indefinido"
4. **Retorno ao início**: Todas as ferramentas iniciais estão disponíveis novamente
### 6. Considerações de Segurança
#### 6.1 Integração de Controle de Acesso
**Filtragem no Nível do Gateway:**
O gateway pode impor restrições de grupo com base nas permissões do usuário
Previne a elevação de privilégios através da manipulação de solicitações
O registro de auditoria inclui os grupos de ferramentas solicitados e concedidos
**Exemplo de Lógica do Gateway:**
```
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 Auditoria e Monitoramento
**Rastreamento de Auditoria Aprimorado:**
Registrar os grupos de ferramentas solicitados e o estado inicial por solicitação
Rastrear as transições de estado e o uso de ferramentas por associação a grupos
Monitorar tentativas de acesso não autorizado a grupos e transições de estado inválidas
Alertar sobre padrões de uso de grupos incomuns ou fluxos de trabalho de estado suspeitos
### 7. Estratégia de Migração
#### 7.1 Compatibilidade com Versões Anteriores
**Fase 1: Alterações Aditivas**
Adicionar campo opcional `group` às configurações de ferramentas
Adicionar campo opcional `group` ao esquema AgentRequest
Comportamento padrão: Todas as ferramentas existentes pertencem ao grupo "default"
Solicitações existentes sem o campo de grupo usam o grupo "default"
**Comportamento Existente Preservado:**
Ferramentas sem configuração de grupo continuam a funcionar (grupo default)
Ferramentas sem configuração de estado estão disponíveis em todos os estados
Solicitações sem especificação de grupo acessam todas as ferramentas (grupo default)
Solicitações sem especificação de estado usam o estado "undefined" (todas as ferramentas disponíveis)
Nenhuma alteração disruptiva para implantações existentes
### 8. Monitoramento e Observabilidade
#### 8.1 Novas Métricas
**Uso de Grupos de Ferramentas:**
`agent_tool_group_requests_total` - Contador de solicitações por grupo
`agent_tool_group_availability` - Indicador do número de ferramentas disponíveis por grupo
`agent_filtered_tools_count` - Histograma do número de ferramentas após a filtragem por grupo + estado
**Métricas de Fluxo de Trabalho de Estado:**
`agent_state_transitions_total` - Contador de transições de estado por ferramenta
`agent_workflow_duration_seconds` - Histograma do tempo gasto em cada estado
`agent_state_availability` - Indicador do número de ferramentas disponíveis por estado
**Métricas de Segurança:**
`agent_group_access_denied_total` - Contador de acessos a grupos não autorizados
`agent_invalid_state_transition_total` - Contador de transições de estado inválidas
`agent_privilege_escalation_attempts_total` - Contador de solicitações suspeitas
#### 8.2 Melhorias de Registro (Logging)
**Registro de Solicitações:**
```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. Estratégia de Testes
#### 9.1 Testes Unitários
**Lógica de Filtragem de Ferramentas:**
Cálculos de interseção de grupos de teste
Lógica de filtragem baseada em estado
Verificar a atribuição padrão de grupo e estado
Testar o comportamento de grupos curinga
Validar o tratamento de grupos vazios
Testar cenários de filtragem combinada de grupo+estado
**Validação de Configuração:**
Testar o carregamento de ferramentas com várias configurações de grupo e estado
Verificar a validação de esquema para especificações inválidas de grupo e estado
Testar a compatibilidade com versões anteriores com as configurações existentes
Validar as definições e ciclos de transição de estado
#### 9.2 Testes de Integração
**Comportamento do Agente:**
Verificar se os agentes veem apenas as ferramentas filtradas por grupo+estado
Testar a execução de solicitações com várias combinações de grupos
Testar as transições de estado durante a execução do agente
Validar o tratamento de erros quando nenhuma ferramenta está disponível
Testar a progressão do fluxo de trabalho por meio de vários estados
**Testes de Segurança:**
Testar a prevenção de escalada de privilégios
Verificar a precisão do registro de auditoria
Testar a integração do gateway com as permissões do usuário
#### 9.3 Cenários de Ponta a Ponta
**Uso Multi-tenant com Fluxos de Trabalho 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
```
**Progressão do Estado do Fluxo de Trabalho:**
```
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. Considerações de Desempenho
#### 10.1 Impacto do Carregamento de Ferramentas
**Carregamento de Configuração:**
Metadados de grupo e estado carregados uma vez na inicialização
Sobrecarga de memória mínima por ferramenta (campos adicionais)
Sem impacto no tempo de inicialização da ferramenta
**Processamento de Requisições:**
Filtragem combinada de grupo+estado ocorre uma vez por requisição
Complexidade O(n) onde n = número de ferramentas configuradas
Transições de estado adicionam uma sobrecarga mínima (atribuição de string)
Impacto insignificante para um número típico de ferramentas (< 100)
#### 10.2 Estratégias de Otimização
**Conjuntos de Ferramentas Pré-calculados:**
Armazene em cache os conjuntos de ferramentas por combinação de grupo+estado
Evite filtragem repetida para padrões comuns de grupo/estado
Troca entre memória e computação para combinações frequentemente usadas
**Carregamento Preguiçoso (Lazy Loading):**
Carregue as implementações das ferramentas somente quando necessário
Reduza o tempo de inicialização para implantações com muitas ferramentas
Registro dinâmico de ferramentas com base nos requisitos do grupo
### 11. Melhorias Futuras
#### 11.1 Atribuição Dinâmica de Grupos
**Agrupamento Contextual:**
Atribua ferramentas a grupos com base no contexto da requisição
Disponibilidade do grupo baseada no tempo (apenas horário comercial)
Restrições de grupo baseadas na carga (ferramentas caras durante baixo uso)
#### 11.2 Hierarquias de Grupos
**Estrutura de Grupo Aninhada:**
```json
{
"knowledge": {
"read": ["knowledge-query", "entity-search"],
"write": ["graph-update", "entity-create"]
}
}
```
#### 11.3 Recomendações de Ferramentas
**Sugestões Baseadas em Grupos:**
Sugerir grupos de ferramentas ideais para tipos de requisições
Aprender com padrões de uso para melhorar as recomendações
Fornecer grupos de fallback quando as ferramentas preferidas não estiverem disponíveis
### 12. Perguntas Abertas
1. **Validação de Grupos**: Nomes de grupos inválidos em requisições devem causar falhas graves ou avisos?
2. **Descoberta de Grupos**: O sistema deve fornecer uma API para listar os grupos disponíveis e suas ferramentas?
3. **Grupos Dinâmicos**: Os grupos devem ser configuráveis em tempo de execução ou apenas na inicialização?
4. **Herança de Grupos**: As ferramentas devem herdar grupos de suas categorias ou implementações pai?
5. **Monitoramento de Desempenho**: Quais métricas adicionais são necessárias para rastrear o uso de ferramentas com base em grupos de forma eficaz?
### 13. Conclusão
O sistema de grupos de ferramentas fornece:
**Segurança**: Controle de acesso granular sobre as capacidades do agente
**Desempenho**: Redução da sobrecarga de carregamento e seleção de ferramentas
**Flexibilidade**: Classificação de ferramentas multidimensional
**Compatibilidade**: Integração perfeita com arquiteturas de agentes existentes
Este sistema permite que as implementações do TrustGraph gerenciem melhor o acesso às ferramentas, aprimorem as fronteiras de segurança e otimizem o uso de recursos, mantendo total compatibilidade com versões anteriores de configurações e requisições.

View file

@ -0,0 +1,479 @@
---
layout: default
title: "Serviços de Ferramentas: Ferramentas de Agente Dinamicamente Plugáveis"
parent: "Portuguese (Beta)"
---
# Serviços de Ferramentas: Ferramentas de Agente Dinamicamente Plugáveis
> **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.
## Status
Implementado
## Visão Geral
Esta especificação define um mecanismo para ferramentas de agente dinamicamente plugáveis, chamadas "serviços de ferramentas". Ao contrário dos tipos de ferramentas integradas existentes (`KnowledgeQueryImpl`, `McpToolImpl`, etc.), os serviços de ferramentas permitem que novas ferramentas sejam introduzidas por:
1. Implantando um novo serviço baseado em Pulsar
2. Adicionando um descritor de configuração que informa ao agente como invocá-lo
Isso permite a extensibilidade sem modificar o framework de resposta do agente principal.
## Terminologia
| Termo | Definição |
|------|------------|
| **Ferramenta Integrada** | Tipos de ferramentas existentes com implementações codificadas em `tools.py` |
| **Serviço de Ferramenta** | Um serviço Pulsar que pode ser invocado como uma ferramenta de agente, definido por um descritor de serviço |
| **Ferramenta** | Uma instância configurada que referencia um serviço de ferramenta, exposta ao agente/LLM |
Este é um modelo de duas camadas, análogo às ferramentas MCP:
MCP: O servidor MCP define a interface da ferramenta → A configuração da ferramenta a referencia
Serviços de Ferramenta: O serviço de ferramenta define a interface Pulsar → A configuração da ferramenta a referencia
## Contexto: Ferramentas Existentes
### Implementação de Ferramenta Integrada
As ferramentas são atualmente definidas em `trustgraph-flow/trustgraph/agent/react/tools.py` com implementações 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 ferramenta:
Possui um serviço Pulsar pré-definido que ele chama (por exemplo, `graph-rag-request`)
Conhece o método exato a ser chamado no cliente (por exemplo, `client.rag()`)
Possui argumentos tipados definidos na implementação
### Registro de Ferramentas (service.py:105-214)
As ferramentas são carregadas da configuração com um campo `type` que mapeia para uma implementação:
```python
if impl_id == "knowledge-query":
impl = functools.partial(KnowledgeQueryImpl, collection=data.get("collection"))
elif impl_id == "text-completion":
impl = TextCompletionImpl
# ... etc
```
## Arquitetura
### Modelo de Duas Camadas
#### Camada 1: Descritor do Serviço de Ferramenta
Um serviço de ferramenta define uma interface de serviço Pulsar. Ele declara:
As filas Pulsar para solicitação/resposta
Os parâmetros de configuração que ele requer das ferramentas que o utilizam
```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}
]
}
```
Um serviço de ferramenta que não requer parâmetros de configuração:
```json
{
"id": "calculator",
"request-queue": "non-persistent://tg/request/calc",
"response-queue": "non-persistent://tg/response/calc",
"config-params": []
}
```
#### Nível 2: Descritor de Ferramenta
Uma ferramenta referencia um serviço de ferramenta e fornece:
Valores de parâmetros de configuração (que satisfazem os requisitos do serviço)
Metadados da ferramenta para o agente (nome, descrição)
Definições de argumentos para o 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últiplas ferramentas podem referenciar o mesmo serviço com diferentes configurações:
```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 da Requisição
Quando uma ferramenta é invocada, a requisição ao serviço da ferramenta inclui:
`user`: Do pedido do agente (multi-tenência)
`config`: Valores de configuração codificados em JSON, provenientes da descrição da ferramenta
`arguments`: Argumentos codificados em JSON, provenientes do LLM
```json
{
"user": "alice",
"config": "{\"collection\": \"customers\"}",
"arguments": "{\"question\": \"What are the top customer complaints?\"}"
}
```
O serviço de ferramenta recebe estes como dicionários analisados no método `invoke`.
### Implementação Genérica do Serviço de Ferramenta
Uma classe `ToolServiceImpl` invoca serviços de ferramenta com base na configuração:
```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)
```
## Decisões de Design
### Modelo de Configuração de Duas Camadas
Os serviços de ferramentas seguem um modelo de duas camadas semelhante às ferramentas MCP:
1. **Serviço de Ferramenta**: Define a interface do serviço Pulsar (tópico, parâmetros de configuração necessários)
2. **Ferramenta**: Referencia um serviço de ferramenta, fornece valores de configuração, define argumentos do LLM
Essa separação permite:
Que um único serviço de ferramenta seja usado por várias ferramentas com diferentes configurações
Uma distinção clara entre a interface do serviço e a configuração da ferramenta
A reutilização das definições de serviço
### Mapeamento de Solicitações: Transmissão com Envelope
A solicitação a um serviço de ferramenta é um envelope estruturado contendo:
`user`: Propagado do pedido do agente para multi-inquilinato
Valores de configuração: Do descritor da ferramenta (por exemplo, `collection`)
`arguments`: Argumentos fornecidos pelo LLM, transmitidos como um dicionário
O gerenciador de agente analisa a resposta do LLM em `act.arguments` como um dicionário (`agent_manager.py:117-154`). Este dicionário é incluído no envelope da solicitação.
### Tratamento de Esquemas: Não Tipados
As solicitações e respostas usam dicionários não tipados. Não há validação de esquema no nível do agente - o serviço de ferramenta é responsável por validar suas entradas. Isso fornece o máximo de flexibilidade para definir novos serviços.
### Interface do Cliente: Tópicos Pulsar Diretos
Os serviços de ferramentas usam tópicos Pulsar diretos, sem a necessidade de configuração de fluxo. O descritor do serviço de ferramenta especifica os nomes completos das filas:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [...]
}
```
Isso permite que os serviços sejam hospedados em qualquer namespace.
### Tratamento de Erros: Convenção de Erro Padrão
As respostas do serviço de ferramenta seguem a convenção de esquema existente com um campo `error`:
```python
@dataclass
class Error:
type: str = ""
message: str = ""
```
Estrutura da resposta:
Sucesso: `error` é `None`, a resposta contém o resultado
Erro: `error` é preenchido com `type` e `message`
Isso corresponde ao padrão usado em todo o serviço existente (por exemplo, `PromptResponse`, `QueryResponse`, `AgentResponse`).
### Correlação de Requisições/Respostas
As requisições e respostas são correlacionadas usando um `id` nas propriedades da mensagem Pulsar:
A requisição inclui `id` nas propriedades: `properties={"id": id}`
A(s) resposta(s) incluem o mesmo `id`: `properties={"id": id}`
Isso segue o padrão existente usado em todo o código-fonte (por exemplo, `agent_service.py`, `llm_service.py`).
### Suporte a Streaming
Os serviços de ferramenta podem retornar respostas em streaming:
Múltiplas mensagens de resposta com o mesmo `id` nas propriedades
Cada resposta inclui o campo `end_of_stream: bool`
A resposta final tem `end_of_stream: True`
Isso corresponde ao padrão usado em `AgentResponse` e outros serviços de streaming.
### Tratamento da Resposta: Retorno de String
Todas as ferramentas existentes seguem o mesmo padrão: **receber argumentos como um dicionário, retornar a observação como uma string**.
| Ferramenta | Tratamento da Resposta |
|------|------------------|
| `KnowledgeQueryImpl` | Retorna `client.rag()` diretamente (string) |
| `TextCompletionImpl` | Retorna `client.question()` diretamente (string) |
| `McpToolImpl` | Retorna uma string, ou `json.dumps(output)` se não for uma string |
| `StructuredQueryImpl` | Formata o resultado para uma string |
| `PromptImpl` | Retorna `client.prompt()` diretamente (string) |
Os serviços de ferramenta seguem o mesmo contrato:
O serviço retorna uma resposta em string (a observação)
Se a resposta não for uma string, ela é convertida via `json.dumps()`
Nenhuma configuração de extração é necessária no descritor
Isso mantém o descritor simples e coloca a responsabilidade de retornar uma resposta de texto apropriada para o agente no serviço.
## Guia de Configuração
Para adicionar um novo serviço de ferramenta, são necessários dois itens de configuração:
### 1. Configuração do Serviço de Ferramenta
Armazenado sob a chave de configuração `tool-service`. Define as filas Pulsar e os parâmetros de configuração disponíveis.
| Campo | Obrigatório | Descrição |
|-------|----------|-------------|
| `id` | Sim | Identificador único para o serviço de ferramenta |
| `request-queue` | Sim | Tópico Pulsar completo para requisições (por exemplo, `non-persistent://tg/request/joke`) |
| `response-queue` | Sim | Tópico Pulsar completo para respostas (por exemplo, `non-persistent://tg/response/joke`) |
| `config-params` | Não | Array de parâmetros de configuração que o serviço aceita |
Cada parâmetro de configuração pode especificar:
`name`: Nome do parâmetro (obrigatório)
`required`: Se o parâmetro deve ser fornecido pelas ferramentas (padrão: falso)
Exemplo:
```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. Configuração da Ferramenta
Armazenado sob a chave de configuração `tool`. Define uma ferramenta que o agente pode usar.
| Campo | Obrigatório | Descrição |
|-------|----------|-------------|
| `type` | Sim | Deve ser `"tool-service"` |
| `name` | Sim | Nome da ferramenta exposto para o LLM |
| `description` | Sim | Descrição do que a ferramenta faz (mostrada para o LLM) |
| `service` | Sim | ID do serviço de ferramenta a ser invocado |
| `arguments` | Não | Array de definições de argumentos para o LLM |
| *(parâmetros de configuração)* | Varia | Quaisquer parâmetros de configuração definidos pelo serviço |
Cada argumento pode especificar:
`name`: Nome do argumento (obrigatório)
`type`: Tipo de dados, por exemplo, `"string"` (obrigatório)
`description`: Descrição mostrada para o LLM (obrigatório)
Exemplo:
```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)"
}
]
}
```
### Carregando a Configuração
Use `tg-put-config-item` para carregar as configurações:
```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
```
O agente-gerenciador deve ser reiniciado para aplicar novas configurações.
## Detalhes da Implementação
### Esquema
Tipos de requisição e resposta em `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: DynamicToolService
Classe base em `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
Implementação em `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)
```
### Arquivos
| Arquivo | Propósito |
|------|---------|
| `trustgraph-base/trustgraph/schema/services/tool_service.py` | Esquemas de requisição/resposta |
| `trustgraph-base/trustgraph/base/tool_service_client.py` | Cliente para invocar serviços |
| `trustgraph-base/trustgraph/base/dynamic_tool_service.py` | Classe base para implementação de serviço |
| `trustgraph-flow/trustgraph/agent/react/tools.py` | Classe `ToolServiceImpl` |
| `trustgraph-flow/trustgraph/agent/react/service.py` | Carregamento de configuração |
### Exemplo: Serviço de Piadas
Um exemplo de serviço em `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}"
```
Configuração do serviço de ferramenta:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [{"name": "style", "required": false}]
}
```
Configuração da ferramenta:
```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"}
]
}
```
### Compatibilidade com versões anteriores
Os tipos de ferramentas integradas existentes continuam a funcionar sem alterações.
`tool-service` é um novo tipo de ferramenta, juntamente com os tipos existentes (`knowledge-query`, `mcp-tool`, etc.).
## Considerações futuras
### Serviços de autoanúncio
Uma melhoria futura poderia permitir que os serviços publicassem seus próprios descritores:
Os serviços publicam em um tópico `tool-descriptors` conhecido na inicialização.
O agente se inscreve e registra dinamicamente as ferramentas.
Permite um verdadeiro sistema de plug-and-play sem alterações de configuração.
Isso está fora do escopo da implementação inicial.
## Referências
Implementação atual da ferramenta: `trustgraph-flow/trustgraph/agent/react/tools.py`
Registro de ferramentas: `trustgraph-flow/trustgraph/agent/react/service.py:105-214`
Esquemas do agente: `trustgraph-base/trustgraph/schema/services/agent.py`

View file

@ -0,0 +1,404 @@
---
layout: default
title: "Decodificador Universal de Documentos"
parent: "Portuguese (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 alimentado por `unstructured` — ingira qualquer formato de documento comum
através de um único serviço com total rastreabilidade e integração com o sistema de gerenciamento de informações,
registrando as posições de origem como metadados de gráfico de conhecimento para
rastreabilidade de ponta a ponta.
## Problema
Atualmente, o TrustGraph possui um decodificador específico para PDF. Suportar formatos adicionais
(DOCX, XLSX, HTML, Markdown, texto simples, PPTX, etc.) requer
ou a escrita de um novo decodificador para cada formato ou a adoção de uma biblioteca de extração universal.
Cada formato tem uma estrutura diferente — alguns são baseados em páginas, outros não — e a cadeia de rastreabilidade
deve registrar de onde em cada documento original cada trecho de texto extraído se originou.
## Abordagem
### Biblioteca: `unstructured`
Utilize `unstructured.partition.auto.partition()`, que detecta automaticamente o formato
a partir do tipo MIME ou da extensão do arquivo e extrai elementos estruturados
(Título, TextoNarrativo, Tabela, ItemDeLista, etc.). Cada elemento carrega
metadados, incluindo:
`page_number` (para formatos baseados em páginas, como PDF, PPTX)
`element_id` (único para cada elemento)
`coordinates` (caixa delimitadora para PDFs)
`text` (o conteúdo de texto extraído)
`category` (tipo de elemento: Título, TextoNarrativo, Tabela, etc.)
### Tipos de Elementos
`unstructured` extrai elementos tipados de documentos. Cada elemento tem
uma categoria e metadados associados:
**Elementos de texto:**
`Title` — títulos de seções
`NarrativeText` — parágrafos do corpo do texto
`ListItem` — itens de listas (com marcadores/numerados)
`Header`, `Footer` — cabeçalhos/rodapés de página
`FigureCaption` — legendas para figuras/imagens
`Formula` — expressões matemáticas
`Address`, `EmailAddress` — informações de contato
`CodeSnippet` — blocos de código (do Markdown)
**Tabelas:**
`Table` — dados tabulares estruturados. `unstructured` fornece ambos
`element.text` (texto simples) e `element.metadata.text_as_html`
(HTML completo `<table>` com linhas, colunas e cabeçalhos preservados).
Para formatos com estrutura de tabela explícita (DOCX, XLSX, HTML), a
extração é altamente confiável. Para PDFs, a detecção de tabelas depende da
estratégia `hi_res` com análise de layout.
**Imagens:**
`Image` — imagens incorporadas detectadas por meio de análise de layout (requer
`hi_res` estratégia). Com `extract_image_block_to_payload=True`,
retorna os dados da imagem como base64 em `element.metadata.image_base64`.
O texto OCR da imagem está disponível em `element.text`.
### Tratamento de Tabelas
Tabelas são um tipo de saída de primeira classe. Quando o decodificador encontra um elemento `Table`,
ele preserva a estrutura HTML em vez de converter para
texto simples. Isso fornece ao extrator LLM downstream uma entrada muito melhor
para extrair conhecimento estruturado de dados tabulares.
O texto da página/seção é montado da seguinte forma:
Elementos de texto: texto simples, unidos por quebras de linha
Elementos de tabela: marcação de tabela HTML de `text_as_html`, envolvida em um
marcador `<table>` para que o LLM possa distinguir tabelas de texto narrativo
Por exemplo, uma página com um título, um parágrafo e uma tabela produz:
```
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>
```
Isso preserva a estrutura da tabela por meio da divisão em partes e no processo de extração
pipeline, onde o LLM pode extrair relacionamentos diretamente de
células estruturadas, em vez de adivinhar o alinhamento das colunas a partir de
espaços em branco.
### Tratamento de Imagens
As imagens são extraídas e armazenadas no sistema como documentos filhos
com `document_type="image"` e um ID `urn:image:{uuid}`. Elas recebem
triplas de procedência com o tipo `tg:Image`, vinculadas à página/seção pai
por meio de `prov:wasDerivedFrom`. Os metadados da imagem (coordenadas,
dimensões, element_id) são registrados na procedência.
**É crucial que as imagens NÃO sejam emitidas como saídas do tipo TextDocument.** Elas são
armazenadas apenas — não enviadas para o chunker ou qualquer processo de texto
pipeline. Isso é intencional:
1. Ainda não existe um pipeline de processamento de imagens (a integração do modelo de visão
é um trabalho futuro).
2. Alimentar dados de imagem em base64 ou fragmentos de OCR no processo de extração de texto
produziria triplas de grafo de conhecimento (KG) inúteis.
As imagens também são excluídas do texto da página montado — quaisquer elementos `Image`
são ignorados silenciosamente ao concatenar os textos dos elementos para uma
página/seção. A cadeia de procedência registra que as imagens existem e onde
elas apareceram no documento, para que possam ser recuperadas por um futuro
pipeline de processamento de imagens sem reingestão do documento.
#### Trabalho futuro
Direcionar entidades `tg:Image` para um modelo de visão para descrição,
interpretação de diagramas ou extração de dados de gráficos.
Armazenar descrições de imagens como documentos de texto filhos que são alimentados no
processo de divisão/extração padrão.
Vincular o conhecimento extraído de volta às imagens de origem por meio da procedência.
### Estratégias de Seção
Para formatos baseados em páginas (PDF, PPTX, XLSX), os elementos são sempre agrupados
por página/slide/planilha primeiro. Para formatos não baseados em páginas (DOCX, HTML, Markdown,
etc.), o decodificador precisa de uma estratégia para dividir o documento em
seções. Isso é configurável em tempo de execução por meio de `--section-strategy`.
Cada estratégia é uma função de agrupamento sobre a lista de `unstructured`
elementos. A saída é uma lista de grupos de elementos; o restante do
pipeline (montagem de texto, armazenamento do bibliotecário, rastreabilidade, emissão de TextDocument
) é idêntico, independentemente da estratégia.
#### `whole-document` (padrão)
Emita todo o documento como uma única seção. Deixe que o
divisor de blocos (chunker) lide com toda a divisão.
Abordagem mais simples, bom ponto de partida
Pode produzir um TextDocument muito grande para arquivos grandes, mas o divisor de blocos
lida com isso
Melhor quando você deseja o máximo de contexto por seção
#### `heading`
Divida nos elementos de cabeçalho (`Title`). Cada seção é um cabeçalho mais
todo o conteúdo até o próximo cabeçalho de nível igual ou superior. Cabeçalhos
aninhados criam seções aninhadas.
Produz unidades coerentes em termos de tópico
Funciona bem para documentos estruturados (relatórios, manuais, especificações)
Fornece ao LLM de extração o contexto do cabeçalho junto com o conteúdo
Recua para `whole-document` se nenhum cabeçalho for encontrado
#### `element-type`
Divida quando o tipo de elemento muda significativamente — especificamente,
comece uma nova seção nas transições entre texto narrativo e tabelas.
Elementos consecutivos da mesma categoria ampla (texto, texto, texto ou
tabela, tabela) permanecem agrupados.
Mantém as tabelas como seções independentes
Bom para documentos com conteúdo misto (relatórios com tabelas de dados)
As tabelas recebem atenção especial na extração
#### `count`
Agrupe um número fixo de elementos por seção. Configurável via
`--section-element-count` (padrão: 20).
Simples e previsível
Não respeita a estrutura do documento
Útil como uma opção de fallback ou para experimentação
#### `size`
Acumula elementos até que um limite de caracteres seja atingido, então inicia uma
nova seção. Respeita os limites dos elementos — nunca divide no meio de um elemento.
Configurável via `--section-max-size` (padrão: 4000 caracteres).
Produz tamanhos de seção aproximadamente uniformes
Respeita os limites dos elementos (ao contrário do "chunker" subsequente)
Bom compromisso entre estrutura e controle de tamanho
Se um único elemento exceder o limite, ele se torna sua própria seção
#### Interação com formatos baseados em página
Para formatos baseados em página, o agrupamento de páginas sempre tem prioridade.
As estratégias de seção podem ser aplicadas opcionalmente *dentro* de uma página se ela for muito
grande (por exemplo, uma página PDF com uma tabela enorme), controlado por
`--section-within-pages` (padrão: falso). Quando falso, cada página é
sempre uma seção, independentemente do tamanho.
### Detecção de Formato
O decodificador precisa saber o tipo MIME do documento para passar para
`unstructured`'s `partition()`. Dois caminhos:
**Caminho do "Librarian"** (`document_id` definido): busca os metadados do documento
do "librarian" primeiro — isso nos dá o `kind` (tipo MIME)
que foi registrado no momento do upload. Em seguida, busca o conteúdo do documento.
Duas chamadas ao "librarian", mas a busca de metadados é leve.
**Caminho "inline"** (compatibilidade com versões anteriores, `data` definido): sem metadados
disponíveis na mensagem. Use `python-magic` para detectar o formato
a partir dos bytes do conteúdo como uma alternativa.
Não são necessárias alterações no esquema `Document` — o bibliotecário já armazena o tipo MIME.
### Arquitetura
Um único serviço `universal-decoder` que:
1. Recebe uma mensagem `Document` (inline ou via referência ao bibliotecário).
2. Se o caminho for o do bibliotecário: busca os metadados do documento (obtém o tipo MIME), então
busca o conteúdo. Se o caminho for inline: detecta o formato a partir dos bytes do conteúdo.
3. Chama `partition()` para extrair os elementos.
4. Agrupa elementos: por página para formatos baseados em página, por estratégia de seção configurada para formatos não baseados em página.
seção.
5. Para cada página/seção:
Gera um ID `urn:page:{uuid}` ou `urn:section:{uuid}`
Monta o texto da página: narrativa como texto simples, tabelas como HTML,
imagens ignoradas
Calcula os deslocamentos de caracteres para cada elemento dentro do texto da página.
Salva no "librarian" como documento filho.
Emite triplas de procedência com metadados posicionais.
Envia `TextDocument` para o processo de divisão em partes (chunking).
6. Para cada elemento de imagem:
Gera um ID `urn:image:{uuid}`.
Salva os dados da imagem no "librarian" como documento filho.
Emite triplas de procedência (armazenadas apenas, não enviadas para o processo de divisão em partes).
### Manipulação de Formato
| Formato | Tipo MIME | Baseado em Página | Notas |
|----------|------------------------------------|------------|--------------------------------|
| PDF | application/pdf | Sim | Agrupamento por página |
| DOCX | application/vnd.openxmlformats... | Não | Usa estratégia de seção |
| PPTX | application/vnd.openxmlformats... | Sim | Agrupamento por slide |
| XLSX/XLS | application/vnd.openxmlformats... | Sim | Agrupamento por planilha |
| HTML | text/html | Não | Usa estratégia de seção |
| Markdown | text/markdown | Não | Usa estratégia de seção |
| Texto Simples | text/plain | Não | Usa estratégia de seção |
| CSV | text/csv | Não | Usa estratégia de seção |
| RST | text/x-rst | Não | Usa estratégia de seção |
| RTF | application/rtf | Não | Usa estratégia de seção |
| ODT | application/vnd.oasis... | Não | Usa estratégia de seção |
| TSV | text/tab-separated-values | Não | Usa estratégia de seção |
### Metadados de Proveniência
Cada entidade de página/seção registra metadados de posição como triplas de proveniência
em `GRAPH_SOURCE`, permitindo rastreabilidade completa desde as triplas do grafo
até as posições no documento de origem.
#### Campos existentes (já em `derived_entity_triples`)
`page_number` — número da página/planilha/slide (indexado a partir de 1, apenas para documentos baseados em página)
`char_offset` — deslocamento de caractere desta página/seção dentro do
texto completo do documento
`char_length` — comprimento do caractere do texto desta página/seção
#### Novos campos (extender `derived_entity_triples`)
`mime_type` — formato original do documento (por exemplo, `application/pdf`)
`element_types` — lista separada por vírgula de categorias de `unstructured`
encontradas nesta página/seção (por exemplo, "Título,TextoNarrativo,Tabela")
`table_count` — número de tabelas nesta página/seção
`image_count` — número de imagens nesta página/seção
Estes requerem novos predicados do namespace 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 imagem: `urn:image:{uuid}`
(`TG_MIME_TYPE` já existe.)
#### Novo tipo de entidade
Para formatos não paginados (DOCX, HTML, Markdown, etc.) onde o decodificador
emite todo o documento como uma única unidade em vez de dividi-lo por
página, a entidade recebe um novo tipo:
```
TG_SECTION_TYPE = "https://trustgraph.ai/ns/Section"
```
Isso distingue seções de páginas ao consultar a procedência:
| Entidade | Tipo | Quando usado |
|----------|-----------------------------|----------------------------------------|
| Documento | `tg:Document` | Arquivo original carregado |
| Página | `tg:Page` | Formatos baseados em páginas (PDF, PPTX, XLSX) |
| Seção | `tg:Section` | Formatos não baseados em páginas (DOCX, HTML, MD, etc.) |
| Imagem | `tg:Image` | Imagens incorporadas (armazenadas, não processadas) |
| Trecho | `tg:Chunk` | Saída do processador de trechos |
| Subgrafo | `tg:Subgraph` | Saída da extração de grafo de conhecimento |
O tipo é definido pelo decodificador com base em se está agrupando por página
ou emitindo uma seção de documento inteiro. `derived_entity_triples` recebe
um parâmetro booleano opcional `section` — quando verdadeiro, a entidade é
classificada como `tg:Section` em vez de `tg:Page`.
#### Cadeia completa de procedência
```
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 link é um conjunto de triplas no grafo nomeado `GRAPH_SOURCE`.
### Configuração do Serviço
Argumentos de linha de comando:
```
--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)
```
Além dos argumentos padrão `FlowProcessor` e da fila de bibliotecários.
### Integração de Fluxo
O decodificador universal ocupa a mesma posição no fluxo de processamento
que o decodificador PDF atual:
```
Document → [universal-decoder] → TextDocument → [chunker] → Chunk → ...
```
Ele registra:
`input` consumidor (schema de documento)
`output` produtor (schema TextDocument)
`triples` produtor (schema de triplas)
Requisição/resposta do bibliotecário (para busca e armazenamento de documentos filhos)
### Implantação
Novo contêiner: `trustgraph-flow-universal-decoder`
Dependência: `unstructured[all-docs]` (inclui PDF, DOCX, PPTX, etc.)
Pode ser executado em conjunto ou substituir o decodificador PDF existente, dependendo
da configuração do fluxo
O decodificador PDF existente permanece disponível para ambientes onde
as dependências `unstructured` são muito pesadas
### O que Muda
| Componento | Alteração |
|------------------------------|-------------------------------------------------|
| `provenance/namespaces.py` | Adicionar `TG_SECTION_TYPE`, `TG_IMAGE_TYPE`, `TG_ELEMENT_TYPES`, `TG_TABLE_COUNT`, `TG_IMAGE_COUNT` |
| `provenance/triples.py` | Adicionar argumentos `mime_type`, `element_types`, `table_count`, `image_count` (kwargs) |
| `provenance/__init__.py` | Exportar novas constantes |
| Novo: `decoding/universal/` | Novo módulo de serviço de decodificação |
| `setup.cfg` / `pyproject` | Adicionar dependência `unstructured[all-docs]` |
| Docker | Nova imagem de contêiner |
| Definições de fluxo | Conectar universal-decoder como entrada de documento |
### O que não muda
Chunker (recebe TextDocument, funciona como antes)
Extratores subsequentes (recebem Chunk, inalterados)
Librarian (armazena documentos filhos, inalterado)
Schema (Document, TextDocument, Chunk inalterados)
Proveniência em tempo de consulta (inalterado)
## Riscos
`unstructured[all-docs]` tem muitas dependências (poppler, tesseract,
libreoffice para alguns formatos). A imagem do contêiner será maior.
Mitigação: oferecer uma variante de `[light]` sem dependências de OCR/office.
Alguns formatos podem produzir extração de texto de baixa qualidade (PDFs digitalizados sem
OCR, layouts complexos de XLSX). Mitigação: parâmetro `strategy` configurável
e o decodificador OCR Mistral existente permanece disponível
para OCR de PDF de alta qualidade.
As atualizações de versão de `unstructured` podem alterar os metadados dos elementos.
Mitigação: fixar a versão, testar a qualidade da extração por formato.

View file

@ -0,0 +1,307 @@
---
layout: default
title: "Gerenciamento do Ciclo de Vida do Vector Store"
parent: "Portuguese (Beta)"
---
# Gerenciamento do Ciclo de Vida do Vector Store
> **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.
## Visão Geral
Este documento descreve como o TrustGraph gerencia coleções de vector store em diferentes implementações de backend (Qdrant, Pinecone, Milvus). O design aborda o desafio de suportar embeddings com diferentes dimensões sem codificar valores de dimensão.
## Declaração do Problema
Os vector stores exigem que a dimensão do embedding seja especificada ao criar coleções/índices. No entanto:
Modelos de embedding diferentes produzem dimensões diferentes (por exemplo, 384, 768, 1536)
A dimensão não é conhecida até que o primeiro embedding seja gerado
Uma única coleção do TrustGraph pode receber embeddings de vários modelos
A codificação de uma dimensão (por exemplo, 384) causa falhas com outros tamanhos de embedding
## Princípios de Design
1. **Criação Preguiçosa (Lazy Creation)**: As coleções são criadas sob demanda durante a primeira escrita, e não durante as operações de gerenciamento de coleção.
2. **Nomeação Baseada em Dimensão**: Os nomes das coleções incluem a dimensão do embedding como um sufixo.
3. **Degradação Graciosa**: As consultas contra coleções inexistentes retornam resultados vazios, e não erros.
4. **Suporte a Múltiplas Dimensões**: Uma única coleção lógica pode ter várias coleções físicas (uma por dimensão).
## Arquitetura
### Convenção de Nomenclatura de Coleções
As coleções de vector store usam sufixos de dimensão para suportar vários tamanhos de embedding:
**Embeddings de Documentos:**
Qdrant: `d_{user}_{collection}_{dimension}`
Pinecone: `d-{user}-{collection}-{dimension}`
Milvus: `doc_{user}_{collection}_{dimension}`
**Embeddings de Grafos:**
Qdrant: `t_{user}_{collection}_{dimension}`
Pinecone: `t-{user}-{collection}-{dimension}`
Milvus: `entity_{user}_{collection}_{dimension}`
Exemplos:
`d_alice_papers_384` - Coleção de artigos da Alice com embeddings de 384 dimensões
`d_alice_papers_768` - Mesma coleção lógica com embeddings de 768 dimensões
`t_bob_knowledge_1536` - Grafo de conhecimento do Bob com embeddings de 1536 dimensões
### Fases do Ciclo de Vida
#### 1. Solicitação de Criação de Coleção
**Fluxo da Solicitação:**
```
User/System → Librarian → Storage Management Topic → Vector Stores
```
**Comportamento:**
O bibliotecário transmite solicitações `create-collection` para todos os backends de armazenamento.
Os processadores de armazenamento vetorial confirmam a solicitação, mas **não criam coleções físicas**.
A resposta é retornada imediatamente com sucesso.
A criação real da coleção é adiada até a primeira escrita.
**Justificativa:**
A dimensão é desconhecida no momento da criação.
Evita a criação de coleções com dimensões incorretas.
Simplifica a lógica de gerenciamento de coleções.
#### 2. Operações de Escrita (Criação Preguiçosa)
**Fluxo de Escrita:**
```
Data → Storage Processor → Check Collection → Create if Needed → Insert
```
**Comportamento:**
1. Extrair a dimensão do embedding do vetor: `dim = len(vector)`
2. Construir o nome da coleção com o sufixo da dimensão
3. Verificar se a coleção existe com essa dimensão específica
4. Se não existir:
Criar a coleção com a dimensão correta
Registrar: `"Lazily creating collection {name} with dimension {dim}"`
5. Inserir o embedding na coleção específica da dimensão
**Cenário de Exemplo:**
```
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. Operações de Consulta
**Fluxo de Consulta:**
```
Query Vector → Determine Dimension → Check Collection → Search or Return Empty
```
**Comportamento:**
1. Extrair a dimensão do vetor de consulta: `dim = len(vector)`
2. Construir o nome da coleção com o sufixo da dimensão
3. Verificar se a coleção existe
4. Se existir:
Realizar uma busca de similaridade
Retornar os resultados
5. Se não existir:
Registrar: `"Collection {name} does not exist, returning empty results"`
Retornar uma lista vazia (nenhum erro é gerado)
**Múltiplas Dimensões na Mesma Consulta:**
Se a consulta contiver vetores de dimensões diferentes
Cada dimensão consulta sua coleção correspondente
Os resultados são agregados
As coleções ausentes são ignoradas (não são tratadas como erros)
**Justificativa:**
Consultar uma coleção vazia é um caso de uso válido
Retornar resultados vazios é semanticamente correto
Evita erros durante a inicialização do sistema ou antes da ingestão de dados
#### 4. Exclusão de Coleção
**Fluxo de Exclusão:**
```
Delete Request → List All Collections → Filter by Prefix → Delete All Matches
```
**Comportamento:**
1. Construir o padrão de prefixo: `d_{user}_{collection}_` (observe o sublinhado no final)
2. Listar todas as coleções no armazenamento vetorial
3. Filtrar as coleções que correspondem ao prefixo
4. Excluir todas as coleções correspondentes
5. Registrar cada exclusão: `"Deleted collection {name}"`
6. Registro resumido: `"Deleted {count} collection(s) for {user}/{collection}"`
**Exemplo:**
```
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
```
**Justificativa:**
Garante a limpeza completa de todas as variantes de dimensão.
A correspondência de padrões evita a exclusão acidental de coleções não relacionadas.
Operação atômica da perspectiva do usuário (todas as dimensões são excluídas juntas).
## Características Comportamentais
### Operações Normais
**Criação de Coleção:**
✓ Retorna sucesso imediatamente.
✓ Nenhum armazenamento físico é alocado.
✓ Operação rápida (sem E/S de backend).
**Primeira Escrita:**
✓ Cria a coleção com a dimensão correta.
✓ Ligeiramente mais lenta devido à sobrecarga da criação da coleção.
✓ Escritas subsequentes na mesma dimensão são rápidas.
**Consultas Antes de Qualquer Escrita:**
✓ Retorna resultados vazios.
✓ Nenhum erro ou exceção.
✓ O sistema permanece estável.
**Escritas de Dimensões Mistas:**
✓ Cria automaticamente coleções separadas por dimensão.
✓ Cada dimensão é isolada em sua própria coleção.
✓ Nenhum conflito de dimensão ou erro de esquema.
**Exclusão de Coleção:**
✓ Remove todas as variantes de dimensão.
✓ Limpeza completa.
✓ Nenhuma coleção órfã.
### Casos Limite
**Múltiplos Modelos de Incorporação:**
```
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
```
**Primeiras Escritas Concorrentes:**
```
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
```
**Migração de Dimensões:**
```
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 Coleção Vazia:**
```
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 Implementação
### Especificidades do Backend de Armazenamento
**Qdrant:**
Usa `collection_exists()` para verificações de existência
Usa `get_collections()` para listagem durante a exclusão
A criação de coleções requer `VectorParams(size=dim, distance=Distance.COSINE)`
**Pinecone:**
Usa `has_index()` para verificações de existência
Usa `list_indexes()` para listagem durante a exclusão
A criação de índices requer esperar pelo status "ready"
Especificação serverless configurada com cloud/região
**Milvus:**
Classes diretas (`DocVectors`, `EntityVectors`) gerenciam o ciclo de vida
Cache interno `self.collections[(dim, user, collection)]` para desempenho
Nomes de coleções são sanitizados (apenas alfanumérico + underscore)
Suporta esquema com IDs de incremento automático
### Considerações de Desempenho
**Latência da Primeira Gravação:**
Overhead adicional devido à criação da coleção
Qdrant: ~100-500ms
Pinecone: ~10-30 segundos (provisionamento serverless)
Milvus: ~500-2000ms (inclui indexação)
**Desempenho da Consulta:**
A verificação de existência adiciona um overhead mínimo (~1-10ms)
Nenhum impacto no desempenho depois que a coleção existe
Cada coleção de dimensões é otimizada independentemente
**Overhead de Armazenamento:**
Metadados mínimos por coleção
O principal overhead é por armazenamento de dimensão
Compromisso: Espaço de armazenamento vs. flexibilidade de dimensão
## Considerações Futuras
**Consolidação Automática de Dimensões:**
Poderia adicionar um processo em segundo plano para identificar e mesclar variantes de dimensões não utilizadas
Requereria re-embedding ou redução de dimensão
**Descoberta de Dimensões:**
Poderia expor uma API para listar todas as dimensões em uso para uma coleção
Útil para administração e monitoramento
**Preferência de Dimensão Padrão:**
Poderia rastrear a "dimensão primária" por coleção
Usar para consultas quando o contexto da dimensão não está disponível
**Quotas de Armazenamento:**
Pode ser necessário limites de dimensão por coleção
Prevenir a proliferação de variantes de dimensão
## Notas de Migração
**Do Sistema de Sufixo de Pré-Dimensão:**
Coleções antigas: `d_{user}_{collection}` (sem sufixo de dimensão)
Coleções novas: `d_{user}_{collection}_{dim}` (com sufixo de dimensão)
Nenhuma migração automática - coleções antigas permanecem acessíveis
Considere um script de migração manual, se necessário
Pode executar ambos os esquemas de nomenclatura simultaneamente
## Referências
Gerenciamento de Coleções: `docs/tech-specs/collection-management.md`
Esquema de Armazenamento: `trustgraph-base/trustgraph/schema/services/storage.py`
Serviço Librarian: `trustgraph-flow/trustgraph/librarian/service.py`