trustgraph/docs/tech-specs/streaming-llm-responses.es.md
Alex Jenkins 8954fa3ad7 Feat: TrustGraph i18n & Documentation Translation Updates (#781)
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.
2026-04-14 12:08:32 +01:00

21 KiB

layout title parent
default Especificación Técnica de Respuestas de LLM en Streaming Spanish (Beta)

Especificación Técnica de Respuestas de LLM en Streaming

Beta Translation: This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.

Resumen

Esta especificación describe la implementación del soporte de streaming para respuestas de LLM en TrustGraph. El streaming permite la entrega en tiempo real de tokens generados a medida que son producidos por el LLM, en lugar de esperar a que se complete la generación de la respuesta.

Esta implementación admite los siguientes casos de uso:

  1. Interfaces de Usuario en Tiempo Real: Transmite tokens a la interfaz de usuario a medida que se generan, proporcionando retroalimentación visual inmediata.
  2. Reducción del Tiempo Hasta el Primer Token: Los usuarios ven la salida inmediatamente en lugar de esperar a que se complete la generación.
  3. Manejo de Respuestas Largas: Maneja salidas muy largas que de otro modo podrían provocar un tiempo de espera o exceder los límites de memoria.
  4. Aplicaciones Interactivas: Permite interfaces de chat y agentes receptivas.

Objetivos

Compatibilidad con Versiones Anteriores: Los clientes existentes que no utilizan streaming continúan funcionando sin modificaciones. Diseño de API Consistente: El streaming y el no streaming utilizan los mismos patrones de esquema con una divergencia mínima. Flexibilidad del Proveedor: Soporte de streaming cuando esté disponible, con una alternativa gradual cuando no esté disponible. Implementación por Fases: Implementación incremental para reducir el riesgo. Soporte de Extremo a Extremo: Streaming desde el proveedor de LLM hasta las aplicaciones cliente a través de Pulsar, la API de Gateway y la API de Python.

Antecedentes

Arquitectura Actual

El flujo actual de finalización de texto de LLM funciona de la siguiente manera:

  1. El cliente envía TextCompletionRequest con los campos system y prompt.
  2. El servicio de LLM procesa la solicitud y espera a que se complete la generación.
  3. Se devuelve un único TextCompletionResponse con la cadena response completa.

Esquema actual (trustgraph-base/trustgraph/schema/services/llm.py):

class TextCompletionRequest(Record):
    system = String()
    prompt = String()

class TextCompletionResponse(Record):
    error = Error()
    response = String()
    in_token = Integer()
    out_token = Integer()
    model = String()

Limitaciones actuales

Latencia: Los usuarios deben esperar a que se complete la generación antes de ver cualquier resultado. Riesgo de tiempo de espera: Las generaciones largas pueden exceder los umbrales de tiempo de espera del cliente. Mala experiencia de usuario: La falta de retroalimentación durante la generación crea la percepción de lentitud. Uso de recursos: Las respuestas completas deben almacenarse en búfer en la memoria.

Esta especificación aborda estas limitaciones al permitir la entrega incremental de respuestas, manteniendo la compatibilidad total con versiones anteriores.

Diseño técnico

Fase 1: Infraestructura

La Fase 1 establece la base para la transmisión mediante la modificación de esquemas, API y herramientas de línea de comandos.

Cambios en el esquema

Esquema de LLM (trustgraph-base/trustgraph/schema/services/llm.py)

Cambios en la solicitud:

class TextCompletionRequest(Record):
    system = String()
    prompt = String()
    streaming = Boolean()  # NEW: Default false for backward compatibility

streaming: Cuando true, solicita la entrega de la respuesta en modo de transmisión. Predeterminado: false (el comportamiento existente se mantiene).

Cambios en la respuesta:

class TextCompletionResponse(Record):
    error = Error()
    response = String()
    in_token = Integer()
    out_token = Integer()
    model = String()
    end_of_stream = Boolean()  # NEW: Indicates final message

end_of_stream: Cuando true, indica que esta es la respuesta final (o única). Para solicitudes no de transmisión: Respuesta única con end_of_stream=true. Para solicitudes de transmisión: Múltiples respuestas, todas con end_of_stream=false excepto la última.

Esquema de la solicitud (trustgraph-base/trustgraph/schema/services/prompt.py)

El servicio de solicitud envuelve la finalización de texto, por lo que refleja el mismo patrón:

Cambios en la solicitud:

class PromptRequest(Record):
    id = String()
    terms = Map(String())
    streaming = Boolean()  # NEW: Default false

Cambios en la respuesta:

class PromptResponse(Record):
    error = Error()
    text = String()
    object = String()
    end_of_stream = Boolean()  # NEW: Indicates final message

Cambios en la API de Gateway

La API de Gateway debe exponer capacidades de transmisión a clientes HTTP/WebSocket.

Actualizaciones de la API REST:

POST /api/v1/text-completion: Aceptar el parámetro streaming en el cuerpo de la solicitud. El comportamiento de la respuesta depende de la bandera de transmisión: streaming=false: Respuesta JSON única (comportamiento actual). streaming=true: Flujo de eventos enviados por el servidor (SSE) o mensajes WebSocket.

Formato de respuesta (transmisión):

Cada fragmento transmitido sigue la misma estructura de esquema:

{
  "response": "partial text...",
  "end_of_stream": false,
  "model": "model-name"
}

Fragmento final:

{
  "response": "final text chunk",
  "end_of_stream": true,
  "in_token": 150,
  "out_token": 500,
  "model": "model-name"
}

Cambios en la API de Python

La API del cliente de Python debe soportar tanto modos de transmisión como no de transmisión manteniendo la compatibilidad con versiones anteriores.

Actualizaciones de LlmClient (trustgraph-base/trustgraph/clients/llm_client.py):

class LlmClient(BaseClient):
    def request(self, system, prompt, timeout=300, streaming=False):
        """
        Non-streaming request (backward compatible).
        Returns complete response string.
        """
        # Existing behavior when streaming=False

    async def request_stream(self, system, prompt, timeout=300):
        """
        Streaming request.
        Yields response chunks as they arrive.
        """
        # New async generator method

Actualizaciones de PromptClient (trustgraph-base/trustgraph/base/prompt_client.py):

Patrón similar con el parámetro streaming y la variante de generador asíncrono.

Cambios en la herramienta de línea de comandos

tg-invoke-llm (trustgraph-cli/trustgraph/cli/invoke_llm.py):

tg-invoke-llm [system] [prompt] [--no-streaming] [-u URL] [-f flow-id]

La transmisión está habilitada de forma predeterminada para una mejor experiencia de usuario interactiva. La opción --no-streaming desactiva la transmisión. Cuando la transmisión está habilitada: envía los tokens a la salida estándar a medida que llegan. Cuando la transmisión no está habilitada: espera la respuesta completa y luego la muestra.

tg-invoke-prompt (trustgraph-cli/trustgraph/cli/invoke_prompt.py):

tg-invoke-prompt [template-id] [var=value...] [--no-streaming] [-u URL] [-f flow-id]

El mismo patrón que tg-invoke-llm.

Cambios en la Clase Base del Servicio LLM

LlmService (trustgraph-base/trustgraph/base/llm_service.py):

class LlmService(FlowProcessor):
    async def on_request(self, msg, consumer, flow):
        request = msg.value()
        streaming = getattr(request, 'streaming', False)

        if streaming and self.supports_streaming():
            async for chunk in self.generate_content_stream(...):
                await self.send_response(chunk, end_of_stream=False)
            await self.send_response(final_chunk, end_of_stream=True)
        else:
            response = await self.generate_content(...)
            await self.send_response(response, end_of_stream=True)

    def supports_streaming(self):
        """Override in subclass to indicate streaming support."""
        return False

    async def generate_content_stream(self, system, prompt, model, temperature):
        """Override in subclass to implement streaming."""
        raise NotImplementedError()

--

Fase 2: Prueba de concepto de VertexAI

La Fase 2 implementa el streaming en un único proveedor (VertexAI) para validar la infraestructura y habilitar pruebas de extremo a extremo.

Implementación de VertexAI

Módulo: trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py

Cambios:

  1. Sobreescribir supports_streaming() para devolver True
  2. Implementar generador asíncrono generate_content_stream()
  3. Manejar tanto los modelos Gemini como Claude (a través de la API de VertexAI Anthropic)

Streaming de Gemini:

async def generate_content_stream(self, system, prompt, model, temperature):
    model_instance = self.get_model(model, temperature)
    response = model_instance.generate_content(
        [system, prompt],
        stream=True  # Enable streaming
    )
    for chunk in response:
        yield LlmChunk(
            text=chunk.text,
            in_token=None,  # Available only in final chunk
            out_token=None,
        )
    # Final chunk includes token counts from response.usage_metadata

Claude (a través de VertexAI Anthropic) en modo de transmisión:

async def generate_content_stream(self, system, prompt, model, temperature):
    with self.anthropic_client.messages.stream(...) as stream:
        for text in stream.text_stream:
            yield LlmChunk(text=text)
    # Token counts from stream.get_final_message()

Pruebas

Pruebas unitarias para el ensamblaje de respuestas en streaming. Pruebas de integración con VertexAI (Gemini y Claude). Pruebas de extremo a extremo: CLI -> Gateway -> Pulsar -> VertexAI -> back. Pruebas de compatibilidad con versiones anteriores: Las solicitudes no en streaming aún funcionan.

--

Fase 3: Todos los proveedores de LLM

La fase 3 extiende el soporte de streaming a todos los proveedores de LLM en el sistema.

Estado de implementación del proveedor

Cada proveedor debe:

  1. Soporte completo de streaming: Implementar generate_content_stream()
  2. Modo de compatibilidad: Manejar la bandera end_of_stream correctamente (devolver una única respuesta con end_of_stream=true)
Proveedor Paquete Soporte de streaming
OpenAI trustgraph-flow Completo (API de streaming nativo)
Claude/Anthropic trustgraph-flow Completo (API de streaming nativo)
Ollama trustgraph-flow Completo (API de streaming nativo)
Cohere trustgraph-flow Completo (API de streaming nativo)
Mistral trustgraph-flow Completo (API de streaming nativo)
Azure OpenAI trustgraph-flow Completo (API de streaming nativo)
Google AI Studio trustgraph-flow Completo (API de streaming nativo)
VertexAI trustgraph-vertexai Completo (Fase 2)
Bedrock trustgraph-bedrock Completo (API de streaming nativo)
LM Studio trustgraph-flow Completo (compatible con OpenAI)
LlamaFile trustgraph-flow Completo (compatible con OpenAI)
vLLM trustgraph-flow Completo (compatible con OpenAI)
TGI trustgraph-flow Por determinar
Azure trustgraph-flow Por determinar

Patrón de implementación

Para proveedores compatibles con OpenAI (OpenAI, LM Studio, LlamaFile, vLLM):

async def generate_content_stream(self, system, prompt, model, temperature):
    response = await self.client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": system},
            {"role": "user", "content": prompt}
        ],
        temperature=temperature,
        stream=True
    )
    async for chunk in response:
        if chunk.choices[0].delta.content:
            yield LlmChunk(text=chunk.choices[0].delta.content)

--

Fase 4: API del Agente

La Fase 4 extiende la transmisión a la API del Agente. Esto es más complejo porque la API del Agente ya es inherentemente multi-mensaje (pensamiento → acción → observación → repetir → respuesta final).

Esquema actual del Agente

class AgentStep(Record):
    thought = String()
    action = String()
    arguments = Map(String())
    observation = String()
    user = String()

class AgentRequest(Record):
    question = String()
    state = String()
    group = Array(String())
    history = Array(AgentStep())
    user = String()

class AgentResponse(Record):
    answer = String()
    error = Error()
    thought = String()
    observation = String()

Cambios Propuestos al Esquema del Agente

Solicitar Cambios:

class AgentRequest(Record):
    question = String()
    state = String()
    group = Array(String())
    history = Array(AgentStep())
    user = String()
    streaming = Boolean()  # NEW: Default false

Cambios en la respuesta:

El agente produce múltiples tipos de salida durante su ciclo de razonamiento: Pensamientos (razonamiento) Acciones (llamadas a herramientas) Observaciones (resultados de las herramientas) Respuesta (respuesta final) Errores

Dado que chunk_type identifica qué tipo de contenido se está enviando, los campos separados answer, error, thought y observation se pueden combinar en un único campo content:

class AgentResponse(Record):
    chunk_type = String()       # "thought", "action", "observation", "answer", "error"
    content = String()          # The actual content (interpretation depends on chunk_type)
    end_of_message = Boolean()  # Current thought/action/observation/answer is complete
    end_of_dialog = Boolean()   # Entire agent dialog is complete

Semántica de los campos:

chunk_type: Indica qué tipo de contenido se encuentra en el campo content "thought": Razonamiento/pensamiento del agente "action": Herramienta/acción que se está invocando "observation": Resultado de la ejecución de la herramienta "answer": Respuesta final a la pregunta del usuario "error": Mensaje de error

content: El contenido transmitido real, interpretado según chunk_type

end_of_message: Cuando true, el tipo de fragmento actual está completo Ejemplo: Todos los tokens para el pensamiento actual han sido enviados Permite a los clientes saber cuándo pasar a la siguiente etapa

end_of_dialog: Cuando true, la interacción completa del agente ha finalizado Este es el mensaje final en la transmisión

Comportamiento de la transmisión del agente

Cuando streaming=true:

  1. Transmisión de pensamientos: Múltiples fragmentos con chunk_type="thought", end_of_message=false El fragmento final del pensamiento tiene end_of_message=true
  2. Notificación de acción: Un solo fragmento con chunk_type="action", end_of_message=true
  3. Observación: Fragmento(s) con chunk_type="observation", el final tiene end_of_message=true
  4. Repetir los pasos 1-3 mientras el agente razona
  5. Respuesta final: chunk_type="answer" con la respuesta final en content El último fragmento tiene end_of_message=true, end_of_dialog=true

Secuencia de ejemplo de la transmisión:

{chunk_type: "thought", content: "I need to", end_of_message: false, end_of_dialog: false}
{chunk_type: "thought", content: " search for...", end_of_message: true, end_of_dialog: false}
{chunk_type: "action", content: "search", end_of_message: true, end_of_dialog: false}
{chunk_type: "observation", content: "Found: ...", end_of_message: true, end_of_dialog: false}
{chunk_type: "thought", content: "Based on this", end_of_message: false, end_of_dialog: false}
{chunk_type: "thought", content: " I can answer...", end_of_message: true, end_of_dialog: false}
{chunk_type: "answer", content: "The answer is...", end_of_message: true, end_of_dialog: true}

Cuando streaming=false: Comportamiento actual preservado Respuesta única con respuesta completa end_of_message=true, end_of_dialog=true

Gateway y API de Python

Gateway: Nuevo punto final SSE/WebSocket para la transmisión de agentes API de Python: Nuevo método de generador asíncrono agent_stream()

--

Consideraciones de seguridad

Sin nueva superficie de ataque: La transmisión utiliza la misma autenticación/autorización Limitación de velocidad: Aplicar límites de velocidad por token o por fragmento si es necesario Manejo de conexiones: Terminar correctamente las transmisiones en caso de desconexión del cliente Gestión de tiempos de espera: Las solicitudes de transmisión requieren un manejo adecuado de los tiempos de espera

Consideraciones de rendimiento

Memoria: La transmisión reduce el uso máximo de memoria (sin almacenamiento en búfer de la respuesta completa) Latencia: El tiempo hasta el primer token se reduce significativamente Sobrecarga de conexión: Las conexiones SSE/WebSocket tienen una sobrecarga de mantenimiento activo Rendimiento de Pulsar: Múltiples mensajes pequeños frente a un mensaje grande único tradeoff

Estrategia de pruebas

Pruebas unitarias

Serialización/deserialización de esquema con nuevos campos Compatibilidad con versiones anteriores (los campos faltantes utilizan los valores predeterminados) Lógica de ensamblaje de fragmentos

Pruebas de integración

Implementación de transmisión de cada proveedor de LLM Puntos finales de transmisión de la API de Gateway Métodos de transmisión del cliente de Python

Pruebas de extremo a extremo

Salida de transmisión de la herramienta de línea de comandos Flujo completo: Cliente → Gateway → Pulsar → LLM → de vuelta Cargas de trabajo mixtas de transmisión/no transmisión

Pruebas de compatibilidad con versiones anteriores

Los clientes existentes funcionan sin modificaciones Las solicitudes no de transmisión se comportan de la misma manera

Plan de migración

Fase 1: Infraestructura

Implementar cambios de esquema (compatible con versiones anteriores) Implementar actualizaciones de la API de Gateway Implementar actualizaciones de la API de Python Lanzar actualizaciones de la herramienta de línea de comandos

Fase 2: VertexAI

Implementar la implementación de transmisión de VertexAI Validar con cargas de trabajo de prueba

Fase 3: Todos los proveedores

Implementar las actualizaciones de los proveedores de forma incremental Monitorear problemas

Fase 4: API de agente

Implementar cambios de esquema de agente Implementar la implementación de transmisión de agente Actualizar la documentación

Cronograma

Fase Descripción Dependencias
Fase 1 Infraestructura Ninguna
Fase 2 PoC de VertexAI Fase 1
Fase 3 Todos los proveedores Fase 2
Fase 4 API de agente Fase 3

Decisiones de diseño

Las siguientes preguntas se resolvieron durante la especificación:

  1. Conteo de tokens en la transmisión: Los conteos de tokens son deltas, no totales acumulados. Los consumidores pueden sumarlos si es necesario. Esto coincide con la forma en que la mayoría de los proveedores informan el uso y simplifica la implementación.

  2. Manejo de errores en la transmisión: Si ocurre un error, el campo error se completa y no se necesitan otros campos. Un error siempre es la comunicación final no se permiten ni se esperan mensajes posteriores después de un error. Para las transmisiones de LLM/Prompt, end_of_stream=true. Para las transmisiones de agente, chunk_type="error" con end_of_dialog=true.

  3. Recuperación de respuesta parcial: El protocolo de mensajería (Pulsar) es resistente, por lo que no se necesita reintento a nivel de mensaje. Si un cliente pierde el seguimiento de la transmisión o se desconecta, debe reintentar la solicitud completa desde cero.

  4. Transmisión del servicio de prompt: La transmisión solo es compatible con texto (text) respuestas, no respuestas estructuradas (object). El servicio de prompt lo sabe desde el principio si la salida será JSON o texto según la plantilla del prompt. Si se realiza una solicitud de transmisión para un prompt de salida JSON, el servicio debe: Devolver el JSON completo en una sola respuesta con end_of_stream=true, o Rechazar la solicitud de transmisión con un error

Preguntas abiertas

Ninguna en este momento.

Referencias

Esquema de LLM actual: trustgraph-base/trustgraph/schema/services/llm.py Esquema de prompt actual: trustgraph-base/trustgraph/schema/services/prompt.py Esquema de agente actual: trustgraph-base/trustgraph/schema/services/agent.py Base del servicio de LLM: trustgraph-base/trustgraph/base/llm_service.py Proveedor de VertexAI: trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py API de Gateway: trustgraph-base/trustgraph/api/ Herramientas de CLI: trustgraph-cli/trustgraph/cli/