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.
24 KiB
| 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 достигают лимита токенов.
Эта реализация поддерживает следующие сценарии использования:
- Извлечение, устойчивое к усечению: Извлекайте допустимые частичные результаты, даже когда вывод LLM усекается в середине ответа. <<<<<<< HEAD
- Извлечение в больших масштабах: Обрабатывайте извлечение большого количества элементов без риска =======
- Масштабное извлечение: Обрабатывайте извлечение большого количества элементов без риска
82edf2d (New md files from RunPod) полной сбоя из-за ограничений на количество токенов.
- Извлечение данных разных типов: Поддерживайте извлечение нескольких типов сущностей (определения, отношения, сущности, атрибуты) в одном запросе.
- Вывод, совместимый со стримингом: Обеспечьте возможность будущей потоковой/инкрементной обработки результатов извлечения.
Цели
Обратная совместимость: Существующие запросы, использующие response-type: "text" и
response-type: "json", продолжают работать без изменений.
Устойчивость к усечению: Частичные выходные данные LLM приводят к частичным, но допустимым результатам,
а не к полному сбою.
Проверка схемы: Поддержка проверки JSON-схемы для отдельных объектов.
Дискриминированные объединения: Поддержка выходных данных смешанных типов с использованием поля type
в качестве дискриминатора.
<<<<<<< HEAD
Минимальные изменения API: Расширение существующей конфигурации запросов с добавлением нового
типа ответа и ключа схемы.
Обзор
======= Минимальные изменения API: Расширение существующей конфигурации запросов с помощью нового типа ответа и ключа схемы.
Контекст
82edf2d (New md files from RunPod)
Текущая архитектура
Сервис запросов поддерживает два типа ответов:
response-type: "text"- Необработанный текстовый ответ, возвращаемый как есть.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: Реализация
- Реализовать метод
parse_jsonl()вPromptManager. - Расширить
invoke()для обработкиresponse-type: "jsonl". - Добавить модульные тесты.
Этап 2: Миграция подсказок
- Обновить подсказку
extract-definitionsи конфигурацию. - Обновить подсказку
extract-relationshipsи конфигурацию. - Обновить подсказку
extract-topicsи конфигурацию. - Обновить подсказку
extract-rowsи конфигурацию. - Обновить подсказку
agent-kg-extractи конфигурацию. - Обновить подсказку
extract-with-ontologiesи конфигурацию.
Этап 3: Обновления для последующих этапов
- Обновить любой код, использующий результаты извлечения, чтобы он мог обрабатывать возвращаемый тип списка.
- Обновить код, который категоризует извлечения смешанных типов, используя поле
type. - Обновить тесты, которые проверяют формат выходных данных извлечения.
Открытые вопросы
На данный момент нет.
Ссылки
Текущая реализация: 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)