trustgraph/docs/tech-specs/streaming-llm-responses.ru.md

579 lines
28 KiB
Markdown
Raw Normal View History

---
layout: default
title: "Техническая спецификация потоковой передачи ответов LLM"
parent: "Russian (Beta)"
---
# Техническая спецификация потоковой передачи ответов LLM
> **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.
## Обзор
Эта спецификация описывает реализацию поддержки потоковой передачи для ответов LLM
в TrustGraph. Потоковая передача обеспечивает доставку сгенерированных
токенов в режиме реального времени по мере их создания LLM, а не после
завершения полной генерации ответа.
Эта реализация поддерживает следующие сценарии использования:
1. **Интерфейсы пользователя в реальном времени**: Передавайте токены в пользовательский интерфейс по мере их генерации,
обеспечивая немедленную визуальную обратную связь.
2. **Сокращение времени до первого токена**: Пользователи видят вывод сразу,
а не после полной генерации.
3. **Обработка очень длинных ответов**: Обрабатывайте очень длинные ответы, которые в противном случае
могли бы привести к таймаутам или превышению лимитов памяти.
4. **Интерактивные приложения**: Обеспечьте отзывчивые чат-интерфейсы и интерфейсы агентов.
## Цели
**Обратная совместимость**: Существующие клиенты, не использующие потоковую передачу, продолжают работать
без изменений.
**Согласованный дизайн API**: Потоковая передача и не потоковая передача используют одни и те же схемы
с минимальными отклонениями.
**Гибкость для поставщиков**: Поддержка потоковой передачи, где это возможно, и плавный
переход к не потоковой передаче, где это невозможно.
**Поэтапное внедрение**: Постепенная реализация для снижения рисков.
**Комплексная поддержка**: Потоковая передача от поставщика LLM до клиентских
приложений через Pulsar, Gateway API и Python API.
## Обзор
### Текущая архитектура
Текущий процесс текстового завершения LLM работает следующим образом:
1. Клиент отправляет `TextCompletionRequest` с полями `system` и `prompt`.
2. Сервис LLM обрабатывает запрос и ожидает полной генерации.
3. Возвращается один `TextCompletionResponse` с полной строкой `response`.
Текущая схема (`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()
```
### Текущие ограничения
**Задержка**: Пользователи должны ждать завершения генерации, прежде чем увидеть какой-либо результат.
**Риск превышения времени ожидания**: Длительная генерация может превысить лимиты времени ожидания клиента.
**Плохой пользовательский опыт**: Отсутствие обратной связи во время генерации создает ощущение медленной работы.
**Использование ресурсов**: Полные ответы должны быть буферизованы в памяти.
Эта спецификация решает эти ограничения, обеспечивая постепенную передачу ответа, при этом сохраняя полную обратную совместимость.
## Техническое проектирование
### Фаза 1: Инфраструктура
Фаза 1 закладывает основу для потоковой передачи путем изменения схем, API и инструментов командной строки.
#### Изменения в схемах
##### Схема LLM (`trustgraph-base/trustgraph/schema/services/llm.py`)
**Изменения в запросах:**
```python
class TextCompletionRequest(Record):
system = String()
prompt = String()
streaming = Boolean() # NEW: Default false for backward compatibility
```
`streaming`: Когда `true`, запрашивается потоковая доставка ответа.
По умолчанию: `false` (сохранено существующее поведение).
**Изменения в ответе:**
```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`: Когда `true`, указывает на то, что это окончательный (или единственный) ответ.
Для нестриминговых запросов: Один ответ с `end_of_stream=true`.
Для стриминговых запросов: Множественные ответы, все с `end_of_stream=false`,
за исключением последнего.
##### Схема запроса (`trustgraph-base/trustgraph/schema/services/prompt.py`)
Сервис запросов оборачивает завершение текста, поэтому он следует той же схеме:
**Изменения в запросе:**
```python
class PromptRequest(Record):
id = String()
terms = Map(String())
streaming = Boolean() # NEW: Default false
```
**Изменения в ответе:**
```python
class PromptResponse(Record):
error = Error()
text = String()
object = String()
end_of_stream = Boolean() # NEW: Indicates final message
```
#### Изменения API шлюза
API шлюз должен предоставлять возможности потоковой передачи данных для клиентов HTTP/WebSocket.
**Обновления REST API:**
`POST /api/v1/text-completion`: Принимать параметр `streaming` в теле запроса
Поведение ответа зависит от флага потоковой передачи:
`streaming=false`: Одиночный ответ в формате JSON (текущее поведение)
`streaming=true`: Поток событий от сервера (SSE) или сообщения WebSocket
**Формат ответа (потоковая передача):**
Каждый фрагмент, передаваемый потоком, имеет одинаковую структуру схемы:
```json
{
"response": "partial text...",
"end_of_stream": false,
"model": "model-name"
}
```
Заключительный раздел:
```json
{
"response": "final text chunk",
"end_of_stream": true,
"in_token": 150,
"out_token": 500,
"model": "model-name"
}
```
#### Изменения в API Python
API клиента Python должен поддерживать как потоковый, так и не потоковый режимы,
при этом сохраняя обратную совместимость.
**Обновления 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
```
**Обновления PromptClient** (`trustgraph-base/trustgraph/base/prompt_client.py`):
Аналогичный шаблон с параметром `streaming` и вариантом асинхронного генератора.
#### Изменения инструмента командной строки
**tg-invoke-llm** (`trustgraph-cli/trustgraph/cli/invoke_llm.py`):
```
tg-invoke-llm [system] [prompt] [--no-streaming] [-u URL] [-f flow-id]
```
По умолчанию включен режим потоковой передачи для улучшения интерактивного пользовательского опыта.
Флаг `--no-streaming` отключает режим потоковой передачи.
В режиме потоковой передачи: выводите токены в стандартный вывод по мере их поступления.
В режиме, когда потоковая передача отключена: дождитесь получения полного ответа, а затем выведите его.
**tg-invoke-prompt** (`trustgraph-cli/trustgraph/cli/invoke_prompt.py`):
```
tg-invoke-prompt [template-id] [var=value...] [--no-streaming] [-u URL] [-f flow-id]
```
Такой же шаблон, как у `tg-invoke-llm`.
#### Изменения базового класса LLM Service.
**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()
```
--
### Фаза 2: Проверка концепции VertexAI
Фаза 2 реализует потоковую передачу данных в одном провайдере (VertexAI) для проверки
инфраструктуры и обеспечения сквозного тестирования.
#### Реализация VertexAI
**Модуль:** `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
**Изменения:**
1. Переопределить `supports_streaming()` для возврата `True`
2. Реализовать асинхронный генератор `generate_content_stream()`
3. Поддержка моделей Gemini и Claude (через VertexAI Anthropic API)
**Потоковая передача данных 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 (через VertexAI Anthropic) в режиме потоковой передачи:**
```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()
```
#### Тестирование
Юнит-тесты для сборки потоковых ответов
Интеграционные тесты с VertexAI (Gemini и Claude)
Комплексные тесты: CLI -> Gateway -> Pulsar -> VertexAI -> back
Тесты обратной совместимости: Непотоковые запросы по-прежнему работают
--
### Фаза 3: Все провайдеры LLM
Фаза 3 расширяет поддержку потоковой передачи для всех провайдеров LLM в системе.
#### Статус реализации для каждого провайдера
Каждый провайдер должен либо:
1. **Полная поддержка потоковой передачи**: Реализовать `generate_content_stream()`
2. **Режим совместимости**: Правильно обрабатывать флаг `end_of_stream`
(возвращать единый ответ с `end_of_stream=true`)
| Провайдер | Пакет | Поддержка потоковой передачи |
|----------|---------|-------------------|
| OpenAI | trustgraph-flow | Полная (нативная API потоковой передачи) |
| Claude/Anthropic | trustgraph-flow | Полная (нативная API потоковой передачи) |
| Ollama | trustgraph-flow | Полная (нативная API потоковой передачи) |
| Cohere | trustgraph-flow | Полная (нативная API потоковой передачи) |
| Mistral | trustgraph-flow | Полная (нативная API потоковой передачи) |
| Azure OpenAI | trustgraph-flow | Полная (нативная API потоковой передачи) |
| Google AI Studio | trustgraph-flow | Полная (нативная API потоковой передачи) |
| VertexAI | trustgraph-vertexai | Полная (Фаза 2) |
| Bedrock | trustgraph-bedrock | Полная (нативная API потоковой передачи) |
| LM Studio | trustgraph-flow | Полная (совместима с OpenAI) |
| LlamaFile | trustgraph-flow | Полная (совместима с OpenAI) |
| vLLM | trustgraph-flow | Полная (совместима с OpenAI) |
| TGI | trustgraph-flow | Будет определено |
| Azure | trustgraph-flow | Будет определено |
#### Шаблон реализации
Для провайдеров, совместимых с 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)
```
--
### Фаза 4: API агента
Фаза 4 расширяет потоковую передачу на API агента. Это более сложный процесс, поскольку
API агента изначально предназначен для работы с несколькими сообщениями (мысль → действие → наблюдение
→ повтор → окончательный ответ).
#### Текущая схема агента
```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()
```
#### Предлагаемые изменения схемы агента
**Запрос изменений:**
```python
class AgentRequest(Record):
question = String()
state = String()
group = Array(String())
history = Array(AgentStep())
user = String()
streaming = Boolean() # NEW: Default false
```
**Изменения в ответах:**
Агент генерирует несколько типов выходных данных в процессе рассуждения:
Мысли (рассуждения)
Действия (вызовы инструментов)
Наблюдения (результаты работы инструментов)
Ответ (окончательный ответ)
Ошибки
Поскольку `chunk_type` указывает на тип передаваемого контента, отдельные
поля `answer`, `error`, `thought` и `observation` можно объединить в
одно поле `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
```
**Семантика полей:**
`chunk_type`: Указывает, какой тип содержимого находится в поле `content`
`"thought"`: Рассуждения/мысли агента
`"action"`: Используемый инструмент/действие
`"observation"`: Результат выполнения инструмента
`"answer"`: Окончательный ответ на вопрос пользователя
`"error"`: Сообщение об ошибке
`content`: Фактическое потоковое содержимое, интерпретируемое на основе `chunk_type`
`end_of_message`: Когда `true`, текущий тип фрагмента завершен
Пример: Все токены для текущей мысли были отправлены
Позволяет клиентам знать, когда переходить к следующему этапу
`end_of_dialog`: Когда `true`, все взаимодействие с агентом завершено
Это последнее сообщение в потоке
#### Поведение потоковой передачи агента
Когда `streaming=true`:
1. **Потоковая передача мыслей:**
Несколько фрагментов с `chunk_type="thought"`, `end_of_message=false`
Последний фрагмент мысли содержит `end_of_message=true`
2. **Уведомление о действии:**
Один фрагмент с `chunk_type="action"`, `end_of_message=true`
3. **Наблюдение:**
Один или несколько фрагментов с `chunk_type="observation"`, последний содержит `end_of_message=true`
4. **Повторяйте** шаги 1-3, пока агент рассуждает
5. **Окончательный ответ:**
`chunk_type="answer"` с окончательным ответом в `content`
Последний фрагмент содержит `end_of_message=true`, `end_of_dialog=true`
**Пример последовательности потоковой передачи:**
```
{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}
```
Когда `streaming=false`:
Текущее поведение сохранено
Единый ответ с полным ответом
`end_of_message=true`, `end_of_dialog=true`
#### Шлюз и Python API
Шлюз: Новый SSE/WebSocket endpoint для потоковой передачи данных от агента
Python API: Новый асинхронный генератор `agent_stream()`
--
## Соображения безопасности
**Отсутствие новых уязвимостей**: Потоковая передача использует ту же аутентификацию/авторизацию
**Ограничение скорости**: При необходимости применяйте ограничения скорости на токен или на фрагмент
**Обработка соединений**: Правильно завершайте потоки при отключении клиента
**Управление временем ожидания**: Запросы потоковой передачи требуют соответствующей обработки времени ожидания
## Соображения производительности
**Память**: Потоковая передача снижает пиковое использование памяти (без полной буферизации ответа)
**Задержка**: Время до первого токена значительно сокращено
**Накладные расходы на соединение**: Соединения SSE/WebSocket имеют накладные расходы на поддержание соединения
**Производительность Pulsar**: Несколько небольших сообщений против одного большого сообщения - компромисс
tradeoff
## Стратегия тестирования
### Юнит-тесты
Сериализация/десериализация схемы с новыми полями
Обратная совместимость (отсутствующие поля используют значения по умолчанию)
Логика сборки фрагментов
### Интеграционные тесты
Реализация потоковой передачи каждого поставщика LLM
Потоковые конечные точки API шлюза
Методы потоковой передачи клиента на Python
### Комплексные тесты
Вывод потоковой передачи инструмента командной строки
Полный поток: Клиент → Шлюз → Pulsar → LLM → обратно
Смешанные потоковые и не потоковые рабочие нагрузки
### Тесты обратной совместимости
Существующие клиенты работают без изменений
Запросы без потоковой передачи ведут себя идентично
## План миграции
### Фаза 1: Инфраструктура
Развертывание изменений схемы (обратная совместимость)
Развертывание обновлений API шлюза
Развертывание обновлений Python API
Выпуск обновлений инструмента командной строки
### Фаза 2: VertexAI
Развернуть поточную реализацию VertexAI.
Проверить с помощью тестовых нагрузок.
### Фаза 3: Все провайдеры
Постепенно внедрять обновления для провайдеров.
Отслеживать наличие проблем.
### Фаза 4: API агента
Развернуть изменения схемы агента.
Развернуть поточную реализацию агента.
Обновить документацию.
## График
| Фаза | Описание | Зависимости |
|-------|-------------|--------------|
| Фаза 1 | Инфраструктура | Отсутствуют |
| Фаза 2 | VertexAI, пилотный проект | Фаза 1 |
| Фаза 3 | Все провайдеры | Фаза 2 |
| Фаза 4 | API агента | Фаза 3 |
## Принятые решения по проектированию
В процессе разработки спецификации были решены следующие вопросы:
1. **Количество токенов в потоке**: Количество токенов указывается как разница, а не как текущая сумма.
Потребители могут суммировать их, если это необходимо. Это соответствует тому, как большинство провайдеров
сообщают об использовании и упрощает реализацию.
2. **Обработка ошибок в потоках**: В случае возникновения ошибки, поле `error`
заполняется, и другие поля не требуются. Ошибка всегда является последним
сообщением - после ошибки не допускаются и не ожидаются последующие сообщения.
Для потоков LLM/Prompt: `end_of_stream=true`. Для потоков Agent:
`chunk_type="error"` с `end_of_dialog=true`.
3. **Восстановление после частичного ответа**: Протокол обмена сообщениями (Pulsar) устойчив,
поэтому повторная отправка сообщений на уровне отдельных сообщений не требуется.
Если клиент теряет отслеживание потока или отключается, он должен повторить
полный запрос с самого начала.
4. **Быстрая потоковая передача**: Потоковая передача поддерживается только для текстовых ответов (`text`).
ответы, а не для структурированных (`object`) ответов. Сервис запросов знает заранее,
будет ли вывод в формате JSON или текста, в зависимости от шаблона запроса. Если
выполняется запрос на потоковую передачу для запроса, предназначенного для вывода JSON,
сервис должен либо:
Вернуть полный JSON в одном ответе с `end_of_stream=true`, или
Отклонить запрос на потоковую передачу с ошибкой.
## Открытые вопросы
На данный момент их нет.
## Ссылки
Текущая схема LLM: `trustgraph-base/trustgraph/schema/services/llm.py`
Текущая схема запросов: `trustgraph-base/trustgraph/schema/services/prompt.py`
Текущая схема агента: `trustgraph-base/trustgraph/schema/services/agent.py`
Базовый URL службы LLM: `trustgraph-base/trustgraph/base/llm_service.py`
Провайдер VertexAI: `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
API шлюза: `trustgraph-base/trustgraph/api/`
Инструменты CLI: `trustgraph-cli/trustgraph/cli/`