Trajectory 格式
Hermes Agent 将对话轨迹(trajectories)保存为兼容 ShareGPT 的 JSONL 格式,用于训练数据、调试制品以及强化学习数据集。
源码文件: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),并包含额外的元数据字段。
你可以通过 save_trajectory() 中的 filename 参数覆盖文件名。
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 数据集时,所有条目的 Schema 保持一致。
Conversations 数组 (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
}
归一化规则
推理内容标记 (Reasoning Content Markup)
轨迹转换器会将所有推理内容归一化到 <think> 标签中,无论模型最初是如何生成的:
-
原生思考 Token(来自 Anthropic、OpenAI o 系列等提供商的
msg["reasoning"]字段):被包装为<think>\n{reasoning}\n</think>\n并置于内容之前。 -
REASONING_SCRATCHPAD XML(当原生思考被禁用,且模型通过系统提示词引导的 XML 进行推理时):
<REASONING_SCRATCHPAD>标签会通过convert_scratchpad_to_think()转换为<think>。 -
空思考块:每个
gpt轮次都保证有一个<think>块。如果没有产生推理,则插入一个空块:<think>\n</think>\n—— 这确保了训练数据的格式一致性。
工具调用归一化
来自 API 格式的工具调用(带有 tool_call_id、函数名、JSON 字符串格式的参数)会被转换为 XML 包装的 JSON:
<tool_call>
{"name": "terminal", "arguments": {"command": "ls -la"}}
</tool_call>
- 参数从 JSON 字符串解析回对象(不进行二次编码)。
- 如果 JSON 解析失败(理论上不应发生,因为在对话期间已验证),则使用空的
{}并记录警告。 - 一个 Assistant 轮次中的多个工具调用会在单个
gpt消息中生成多个<tool_call>块。
工具响应归一化
紧随 Assistant 消息之后的所有工具结果都会被合并到一个 tool 轮次中,并使用 XML 包装的 JSON 响应:
<tool_response>
{"tool_call_id": "call_abc123", "name": "terminal", "content": "output here"}
</tool_response>
- 如果工具内容看起来像 JSON(以
{或[开头),它会被解析,以便content字段包含 JSON 对象/数组而不是字符串。 - 多个工具结果在一条消息中以换行符连接。
- 工具名称通过位置与父级 Assistant 的
tool_calls数组进行匹配。
系统消息
系统消息是在保存时生成的(不是直接从对话中提取的)。它遵循 Hermes 函数调用提示词模板,包含:
- 说明函数调用协议的前导语。
- 包含 JSON 工具定义的
<tools>XML 块。 FunctionCall对象的 Schema 引用。<tool_call>示例。
工具定义包括 name、description、parameters 和 required(设置为 null 以符合规范格式)。
加载轨迹
轨迹文件是标准的 JSONL 格式 —— 可以使用任何 JSON-lines 读取器加载:
import json
def load_trajectories(path: str):
"""从 JSONL 文件加载轨迹条目。"""
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
# 仅过滤成功完成的条目
successful = [e for e in load_trajectories("trajectory_samples.jsonl")
if e.get("completed")]
# 仅提取对话用于训练
training_data = [e["conversations"] for e in successful]
为 HuggingFace Datasets 加载
from datasets import load_dataset
ds = load_dataset("json", data_files="trajectory_samples.jsonl")
归一化的 tool_stats Schema 确保所有条目具有相同的列,从而防止在加载数据集时出现 Arrow Schema 不匹配错误。
控制轨迹保存
在 CLI 中,轨迹保存由以下配置控制:
# config.yaml
agent:
save_trajectories: true # 默认值: false
或者通过 --save-trajectories 标志控制。当 Agent 以 save_trajectories=True 初始化时,在每个对话轮次结束时都会调用 _save_trajectory() 方法。
Batch runner 始终会保存轨迹(这是它的主要用途)。
所有轮次中推理内容均为零的样本会被 Batch runner 自动丢弃,以避免非推理示例污染训练数据。