mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 08:26:21 +02:00
Native CLI i18n: The TrustGraph CLI has built-in translation support that dynamically loads language strings. You can test and use different languages by simply passing the --lang flag (e.g., --lang es for Spanish, --lang ru for Russian) or by configuring your environment's LANG variable. Automated Docs Translations: This PR introduces autonomously translated Markdown documentation into several target languages, including Spanish, Swahili, Portuguese, Turkish, Hindi, Hebrew, Arabic, Simplified Chinese, and Russian.
504 lines
18 KiB
Markdown
504 lines
18 KiB
Markdown
---
|
|
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`)
|