mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-26 00:46:22 +02:00
579 lines
20 KiB
Markdown
579 lines
20 KiB
Markdown
|
|
---
|
||
|
|
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/`
|