Формат траекторий
Hermes Agent сохраняет траектории диалогов в формате JSONL, совместимом с ShareGPT, для использования в качестве обучающих данных, артефактов отладки и наборов данных для обучения с подкреплением.
Исходные файлы: agent/trajectory.py, run_agent.py (поиск _save_trajectory), batch_runner.py
Соглашение об именовании файлов
Траектории записываются в файлы в текущей рабочей директории:
| Файл | Когда |
|---|---|
trajectory_samples.jsonl |
Диалоги, успешно завершённые (completed=True) |
failed_trajectories.jsonl |
Диалоги, завершившиеся ошибкой или прерванные (completed=False) |
Пакетный обработчик (batch_runner.py) записывает результаты в пользовательский выходной файл для каждого пакета
(например, batch_001_output.jsonl) с дополнительными полями метаданных.
Вы можете переопределить имя файла с помощью параметра filename в save_trajectory().
Формат записи JSONL
Каждая строка в файле представляет собой самодостаточный JSON-объект. Существует два варианта:
Формат CLI/интерактивный (из _save_trajectory)
{
"conversations": [ ... ],
"timestamp": "2026-03-30T14:22:31.456789",
"model": "anthropic/claude-sonnet-4.6",
"completed": true
}
Формат пакетного обработчика (из batch_runner.py)
{
"prompt_index": 42,
"conversations": [ ... ],
"metadata": { "prompt_source": "gsm8k", "difficulty": "hard" },
"completed": true,
"partial": false,
"api_calls": 7,
"toolsets_used": ["code_tools", "file_tools"],
"tool_stats": {
"terminal": {"count": 3, "success": 3, "failure": 0},
"read_file": {"count": 2, "success": 2, "failure": 0},
"write_file": {"count": 0, "success": 0, "failure": 0}
},
"tool_error_counts": {
"terminal": 0,
"read_file": 0,
"write_file": 0
}
}
Словари tool_stats и tool_error_counts нормализованы и включают
ВСЕ возможные инструменты (из model_tools.TOOL_TO_TOOLSET_MAP) с нулевыми значениями по умолчанию,
что обеспечивает согласованную схему для всех записей при загрузке датасетов HuggingFace.
Массив диалогов (формат ShareGPT)
Массив conversations использует соглашения о ролях ShareGPT:
| Роль API | ShareGPT from |
|---|---|
| system | "system" |
| user | "human" |
| assistant | "gpt" |
| tool | "tool" |
Полный пример
{
"conversations": [
{
"from": "system",
"value": "You are a function calling AI model. You are provided with function signatures within <tools> </tools> XML tags. You may call one or more functions to assist with the user query. If available tools are not relevant in assisting with user query, just respond in natural conversational language. Don't make assumptions about what values to plug into functions. After calling & executing the functions, you will be provided with function results within <tool_response> </tool_response> XML tags. Here are the available tools:\\n<tools>\\n[{\\"name\\": \\"terminal\\", \\"description\\": \\"Execute shell commands\\", \\"parameters\\": {\\"type\\": \\"object\\", \\"properties\\": {\\"command\\": {\\"type\\": \\"string\\"}}}, \\"required\\": null}]\\n</tools>\\nFor each function call return a JSON object, with the following pydantic model json schema for each:\\n{'title': 'FunctionCall', 'type': 'object', 'properties': {'name': {'title': 'Name', 'type': 'string'}, 'arguments': {'title': 'Arguments', 'type': 'object'}}, 'required': ['name', 'arguments']}\\nEach function call should be enclosed within <tool_call> </tool_call> XML tags.\\nExample:\\n<tool_call>\\n{'name': <function-name>,'arguments': <args-dict>}\\n</tool_call>"
},
{
"from": "human",
"value": "What Python version is installed?"
},
{
"from": "gpt",
"value": "<think>\\nThe user wants to know the Python version. I should run python3 --version.\\n</think>\\n<tool_call>\\n{\\"name\\": \\"terminal\\", \\"arguments\\": {\\"command\\": \\"python3 --version\\"}}\\n</tool_call>"
},
{
"from": "tool",
"value": "<tool_response>\\n{\\"tool_call_id\\": \\"call_abc123\\", \\"name\\": \\"terminal\\", \\"content\\": \\"Python 3.11.6\\"}\\n</tool_response>"
},
{
"from": "gpt",
"value": "<think>\\nGot the version. I can now answer the user.\\n</think>\\nPython 3.11.6 is installed on this system."
}
],
"timestamp": "2026-03-30T14:22:31.456789",
"model": "anthropic/claude-sonnet-4.6",
"completed": true
}
Правила нормализации
Разметка контента рассуждений
Конвертер траекторий нормализует ВСЕ рассуждения в теги <think>, независимо от того,
как модель изначально их сгенерировала:
-
Нативные токены рассуждений (поле
msg["reasoning"]от провайдеров, таких как Anthropic, серия OpenAI o): Оборачиваются в<think>\n{reasoning}\n</think>\nи добавляются перед содержимым. -
XML REASONING_SCRATCHPAD (когда нативное мышление отключено и модель рассуждает через XML, заданный системным промптом): Теги
<REASONING_SCRATCHPAD>преобразуются в<think>с помощьюconvert_scratchpad_to_think(). -
Пустые блоки think: Каждый ход
gptгарантированно содержит блок<think>. Если рассуждения не были сгенерированы, вставляется пустой блок:<think>\n</think>\n— это обеспечивает согласованный формат для обучающих данных.
Нормализация вызовов инструментов
Вызовы инструментов из формата API (с tool_call_id, именем функции, аргументами в виде
JSON-строки) преобразуются в JSON, обёрнутый в XML:
<tool_call>
{"name": "terminal", "arguments": {"command": "ls -la"}}
</tool_call>
-
Аргументы парсятся из JSON-строк обратно в объекты (без двойного кодирования)
-
Если парсинг JSON завершается ошибкой (не должно происходить — валидируется в ходе диалога), используется пустой
{}с записью предупреждения в лог -
Множественные вызовы инструментов в одном ходе ассистента формируют несколько блоков
<tool_call>в одном сообщенииgpt
Нормализация ответов инструментов
Все результаты работы инструментов, следующие за сообщением ассистента, группируются в один ход
tool с XML-обёрткой для JSON-ответов:
<tool_response>
{"tool_call_id": "call_abc123", "name": "terminal", "content": "output here"}
</tool_response>
-
Если содержимое инструмента выглядит как JSON (начинается с
{или[), оно парсится так, чтобы поле content содержало JSON-объект или массив, а не строку -
Множественные результаты инструментов объединяются с помощью символов новой строки в одном сообщении
-
Имя инструмента сопоставляется по позиции с массивом
tool_callsродительского ассистента
Системное сообщение
Системное сообщение генерируется в момент сохранения (не берётся из диалога). Оно следует шаблону промпта вызова функций Hermes и содержит:
-
Вступление, объясняющее протокол вызова функций
-
XML-блок
<tools>, содержащий JSON-определения инструментов -
Ссылку на схему для объектов
FunctionCall -
Пример
<tool_call>
Определения инструментов включают name, description, parameters и required
(установлено в null для соответствия каноническому формату).
Загрузка траекторий
Траектории — это стандартный JSONL. Загружайте любым JSON-ридером для строк:
import json
def load_trajectories(path: str):
"""Load trajectory entries from a JSONL file."""
entries = []
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line:
entries.append(json.loads(line))
return entries
# Filter to successful completions only
successful = [e for e in load_trajectories("trajectory_samples.jsonl")
if e.get("completed")]
# Extract just the conversations for training
training_data = [e["conversations"] for e in successful]
Загрузка для датасетов HuggingFace
from datasets import load_dataset
ds = load_dataset("json", data_files="trajectory_samples.jsonl")
Нормализованная схема tool_stats гарантирует, что все записи имеют одинаковые столбцы,
предотвращая ошибки несоответствия схемы Arrow при загрузке датасета.
Управление сохранением траекторий
В CLI сохранение траекторий управляется следующим образом:
# config.yaml
agent:
save_trajectories: true # по умолчанию: false
Или с помощью флага --save-trajectories. Когда агент инициализируется с
save_trajectories=True, метод _save_trajectory() вызывается в конце
каждого хода диалога.
Пакетный обработчик всегда сохраняет траектории (это его основная задача).
Сэмплы без единого рассуждения на всех ходах автоматически отбрасываются пакетным обработчиком, чтобы избежать загрязнения обучающих данных примерами без рассуждений.