trustgraph/docs/tech-specs/jsonl-prompt-output.ru.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

24 KiB
Raw Blame History

layout title parent
default Техническая спецификация формата вывода JSONL для запросов Russian (Beta)

Техническая спецификация формата вывода 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.

Обзор

Эта спецификация описывает реализацию формата вывода JSONL (JSON Lines) для ответов на запросы в TrustGraph. JSONL обеспечивает устойчивость к усечению при извлечении структурированных данных из ответов LLM, решая критические проблемы, связанные с повреждением выходных массивов JSON, когда ответы LLM достигают лимита токенов.

Эта реализация поддерживает следующие сценарии использования:

  1. Извлечение, устойчивое к усечению: Извлекайте допустимые частичные результаты, даже когда вывод LLM усекается в середине ответа. <<<<<<< HEAD
  2. Извлечение в больших масштабах: Обрабатывайте извлечение большого количества элементов без риска =======
  3. Масштабное извлечение: Обрабатывайте извлечение большого количества элементов без риска

82edf2d (New md files from RunPod) полной сбоя из-за ограничений на количество токенов.

  1. Извлечение данных разных типов: Поддерживайте извлечение нескольких типов сущностей (определения, отношения, сущности, атрибуты) в одном запросе.
  2. Вывод, совместимый со стримингом: Обеспечьте возможность будущей потоковой/инкрементной обработки результатов извлечения.

Цели

Обратная совместимость: Существующие запросы, использующие response-type: "text" и response-type: "json", продолжают работать без изменений. Устойчивость к усечению: Частичные выходные данные LLM приводят к частичным, но допустимым результатам, а не к полному сбою. Проверка схемы: Поддержка проверки JSON-схемы для отдельных объектов. Дискриминированные объединения: Поддержка выходных данных смешанных типов с использованием поля type в качестве дискриминатора. <<<<<<< HEAD Минимальные изменения API: Расширение существующей конфигурации запросов с добавлением нового типа ответа и ключа схемы.

Обзор

======= Минимальные изменения API: Расширение существующей конфигурации запросов с помощью нового типа ответа и ключа схемы.

Контекст

82edf2d (New md files from RunPod)

Текущая архитектура

Сервис запросов поддерживает два типа ответов:

  1. response-type: "text" - Необработанный текстовый ответ, возвращаемый как есть.
  2. response-type: "json" - JSON, полученный из ответа и проверенный на соответствие необязательной schema.

Текущая реализация в trustgraph-flow/trustgraph/template/prompt_manager.py:

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

Текущие ограничения

Когда запросы извлечения требуют вывод в виде массивов JSON ([{...}, {...}, ...]):

Повреждение из-за усечения: Если языковая модель достигает лимита выходных токенов в середине массива, весь ответ становится недействительным JSON и не может быть проанализирован. Анализ "все или ничего": Необходимо получить полный вывод перед анализом. Нет частичных результатов: Усеченный ответ дает нулевые полезные данные. Ненадежно для больших извлечений: Чем больше извлеченных элементов, тем выше риск сбоя.

Данная спецификация решает эти ограничения, вводя формат JSONL для запросов извлечения, где каждый извлеченный элемент является полным JSON-объектом на своей строке.

Техническое проектирование

Расширение типа ответа

Добавьте новый тип ответа "jsonl" наряду с существующими типами "text" и "json".

Изменения конфигурации

Новое значение типа ответа:

"response-type": "jsonl"

Интерпретация схемы:

Существующий ключ "schema" используется как для "json", так и для "jsonl" типов ответов. Интерпретация зависит от типа ответа:

"json": Схема описывает весь ответ (обычно массив или объект). "jsonl": Схема описывает каждую отдельную строку/объект.

{
  "response-type": "jsonl",
  "schema": {
    "type": "object",
    "properties": {
      "entity": { "type": "string" },
      "definition": { "type": "string" }
    },
    "required": ["entity", "definition"]
  }
}

Это позволяет избежать изменений в инструментах и редакторах конфигурации подсказок.

Спецификация формата JSONL

Простое извлечение

Для подсказок, извлекающих один тип объекта (определения, отношения, темы, строки), вывод представляет собой один объект JSON на строку без обертки:

Формат вывода подсказки:

{"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"}
{"entity": "chlorophyll", "definition": "Green pigment in plants"}
{"entity": "mitochondria", "definition": "Powerhouse of the cell"}

Контраст с предыдущим форматом JSON-массива:

[
  {"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"},
  {"entity": "chlorophyll", "definition": "Green pigment in plants"},
  {"entity": "mitochondria", "definition": "Powerhouse of the cell"}
]

Если LLM обрезает текст после строки 2, формат массива JSON приводит к недействительному JSON, в то время как JSONL дает два допустимых объекта.

Извлечение данных смешанных типов (дискриминированные объединения)

Для запросов, извлекающих несколько типов объектов (например, определения и отношения, или сущности, отношения и атрибуты), используйте поле "type" в качестве дискриминатора:

Формат вывода запроса:

{"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}

Схема для дискриминированных объединений использует oneOf:

{
  "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"]
      }
    ]
  }
}

Извлечение онтологии

Для извлечения онтологии на основе сущностей, связей и атрибутов:

Формат вывода запроса:

{"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"}

Детали реализации

Класс Prompt

Существующий класс Prompt не требует изменений. Поле schema используется повторно <<<<<<< HEAD для JSONL, а его интерпретация определяется response_type:

для JSONL, и его интерпретация определяется response_type:

82edf2d (New md files from RunPod)

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

Изменения не требуются - существующая загрузка конфигурации уже обрабатывает ключ schema.

Разбор JSONL

Добавлен новый метод разбора ответов в формате JSONL:

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

Изменения в PromptManager.invoke

Расширить метод invoke для обработки нового типа ответа:

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")

Затронутые запросы

Следующие запросы должны быть перенесены в формат JSONL:

Идентификатор запроса Описание Тип поля
extract-definitions Извлечение сущностей/определений Нет (один тип)
extract-relationships Извлечение отношений Нет (один тип)
extract-topics Извлечение темы/определения Нет (один тип)
extract-rows Извлечение структурированных строк Нет (один тип)
agent-kg-extract Комбинированное извлечение определений + отношений Да: "definition", "relationship"
extract-with-ontologies / ontology-extract Извлечение на основе онтологии Да: "entity", "relationship", "attribute"

Изменения API

Точка зрения клиента

Разбор JSONL прозрачен для вызывающих API сервиса запросов. Разбор происходит на стороне сервера в сервисе запросов, и ответ возвращается через стандартное поле PromptResponse.object в виде сериализованного массива JSON.

Когда клиенты вызывают сервис запросов (через PromptClient.prompt() или аналогично):

response-type: "json" с схемой массива → клиент получает Python list response-type: "jsonl" → клиент получает Python list

С точки зрения клиента, оба возвращают идентичные структуры данных. Разница заключается только в том, как вывод LLM разбирается на стороне сервера:

Формат массива JSON: Один вызов json.loads(); полностью завершается с ошибкой, если усечен Формат JSONL: Разбор построчно; дает частичные результаты, если усечен

Это означает, что существующий клиентский код, ожидающий список от запросов извлечения, не требует изменений при переносе запросов из формата JSON в формат JSONL.

Возвращаемое значение сервера

Для response-type: "jsonl" метод PromptManager.invoke() возвращает list[dict], содержащий все успешно разобранные и проверенные объекты. Этот список затем сериализуется в JSON для поля PromptResponse.object.

Обработка ошибок

Пустые результаты: Возвращает пустой список [] с предупреждением в журнале Частная ошибка разбора: Возвращает список успешно разобранных объектов с предупреждениями в журналах об ошибках Полная ошибка разбора: Возвращает пустой список [] с предупреждениями в журналах

Это отличается от response-type: "json", который вызывает RuntimeError при ошибке разбора. Легкое поведение для JSONL намеренно, чтобы обеспечить устойчивость к усечению.

Пример конфигурации

Полный пример конфигурации запроса:

{
  "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"]
  }
}

Соображения безопасности

Проверка входных данных: Разбор JSON использует стандартную json.loads(), что безопасно от атак внедрения. Проверка схемы: Используется jsonschema.validate() для обеспечения соответствия схеме. Отсутствие новых уязвимостей: Разбор JSONL значительно безопаснее, чем разбор JSON-массивов, благодаря обработке построчно.

Соображения производительности

Память: Построчный разбор использует меньше пиковой памяти, чем загрузка полных JSON-массивов. Задержка: Производительность разбора сопоставима с разбором JSON-массивов. Проверка: Проверка схемы выполняется для каждого объекта, что добавляет накладные расходы, но позволяет получать частичные результаты в случае сбоя проверки.

Стратегия тестирования

Юнит-тесты

Разбор JSONL с допустимыми входными данными. Разбор JSONL с пустыми строками. <<<<<<< HEAD Разбор JSONL с блоками форматированного текста Markdown.

Разбор JSONL с блоками кода Markdown.

82edf2d (New md files from RunPod) Разбор JSONL с обрезанной последней строкой. Разбор JSONL со строками, содержащими недопустимый JSON. Проверка схемы с использованием oneOf для дискриминируемых объединений. Обратная совместимость: существующие "text" и "json" подсказки не изменены.

Интеграционные тесты

<<<<<<< HEAD Комплексная извлечение данных с использованием подсказок JSONL. Извлечение данных с имитацией усечения (искусственно ограниченный ответ). Извлечение данных смешанных типов с использованием дискриминатора типов. Извлечение данных онтологии со всеми тремя типами.

Тесты качества извлечения данных.

======= Комплексная извлечение с подсказками JSONL. Извлечение с имитацией усечения (искусственно ограниченный ответ). Извлечение смешанных типов с использованием дискриминатора типа. Извлечение онтологии со всеми тремя типами.

Тесты качества извлечения.

82edf2d (New md files from RunPod)

Сравнение результатов извлечения: формат JSONL против массива JSON. Проверка устойчивости к усечению: JSONL возвращает частичные результаты, в то время как JSON - нет.

План миграции

Этап 1: Реализация

  1. Реализовать метод parse_jsonl() в PromptManager.
  2. Расширить invoke() для обработки response-type: "jsonl".
  3. Добавить модульные тесты.

Этап 2: Миграция подсказок

  1. Обновить подсказку extract-definitions и конфигурацию.
  2. Обновить подсказку extract-relationships и конфигурацию.
  3. Обновить подсказку extract-topics и конфигурацию.
  4. Обновить подсказку extract-rows и конфигурацию.
  5. Обновить подсказку agent-kg-extract и конфигурацию.
  6. Обновить подсказку extract-with-ontologies и конфигурацию.

Этап 3: Обновления для последующих этапов

  1. Обновить любой код, использующий результаты извлечения, чтобы он мог обрабатывать возвращаемый тип списка.
  2. Обновить код, который категоризует извлечения смешанных типов, используя поле type.
  3. Обновить тесты, которые проверяют формат выходных данных извлечения.

Открытые вопросы

На данный момент нет.

Ссылки

Текущая реализация: trustgraph-flow/trustgraph/template/prompt_manager.py Спецификация JSON Lines: https://jsonlines.org/ Схема JSON oneOf: https://json-schema.org/understanding-json-schema/reference/combining.html#oneof Связанная спецификация: Streaming LLM Responses (docs/tech-specs/streaming-llm-responses.md)