Hermes Agent 会以兼容 ShareGPT 的 JSONL 格式保存 conversation trajectories,用作 training data、debugging artifacts 和 reinforcement learning datasets。
源文件:agent/trajectory.py、run_agent.py(搜索 _save_trajectory)、batch_runner.py。
文件命名约定
Section titled “文件命名约定”Trajectories 会被写入当前 working directory 中的文件:
| File | When |
|---|---|
trajectory_samples.jsonl | 成功完成的 conversations(completed=True) |
failed_trajectories.jsonl | 失败或被中断的 conversations(completed=False) |
batch runner(batch_runner.py)会为每个 batch 写入一个自定义 output file(例如 batch_001_output.jsonl),并带有额外的 metadata fields。
你可以通过 save_trajectory() 中的 filename 参数覆盖文件名。
JSONL Entry Format
Section titled “JSONL Entry Format”文件中的每一行都是一个自包含 JSON object。有两种变体:
CLI / Interactive Format(来自 _save_trajectory)
Section titled “CLI / Interactive Format(来自 _save_trajectory)”{ "conversations": [ ... ], "timestamp": "2026-03-30T14:22:31.456789", "model": "anthropic/claude-sonnet-4.6", "completed": true}Batch Runner Format(来自 batch_runner.py)
Section titled “Batch Runner Format(来自 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 dictionaries 会被规范化,以包含所有可能的 tools(来自 model_tools.TOOL_TO_TOOLSET_MAP),并使用零作为默认值,从而确保 HuggingFace dataset loading 时 entries 之间的 schema 一致。
Conversations Array(ShareGPT Format)
Section titled “Conversations Array(ShareGPT Format)”conversations array 使用 ShareGPT role conventions:
| API Role | 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}Normalization Rules
Section titled “Normalization Rules”Reasoning Content Markup
Section titled “Reasoning Content Markup”trajectory converter 会将所有 reasoning 规范化为 <think> tags,无论 model 最初如何生成它:
-
Native thinking tokens(来自 Anthropic、OpenAI o-series 等 providers 的
msg["reasoning"]字段):包装为<think>\n{reasoning}\n</think>\n,并前置到 content 之前。 -
REASONING_SCRATCHPADXML(当 native thinking 被禁用,且 model 通过 system-prompt-instructed XML 进行 reasoning 时):<REASONING_SCRATCHPAD>tags 会通过convert_scratchpad_to_think()转换为<think>。 -
Empty think blocks:每个
gptturn 都保证有一个<think>block。如果没有生成 reasoning,会插入一个空 block:<think>\n</think>\n—— 这确保 training data 格式一致。
Tool Call Normalization
Section titled “Tool Call Normalization”来自 API format 的 tool calls(带有 tool_call_id、function name、作为 JSON string 的 arguments)会被转换为 XML-wrapped JSON:
<tool_call>{"name": "terminal", "arguments": {"command": "ls -la"}}</tool_call>-
Arguments 会从 JSON strings 解析回 objects(不会被 double-encoded)
-
如果 JSON parsing 失败(不应该发生 —— conversation 期间已验证),会使用空
{},并记录 warning -
同一个 assistant turn 中的多个 tool calls 会在单个
gptmessage 中生成多个<tool_call>blocks
Tool Response Normalization
Section titled “Tool Response Normalization”跟在 assistant message 后面的所有 tool results 会被分组为一个单独的 tool turn,并使用 XML-wrapped JSON responses:
<tool_response>{"tool_call_id": "call_abc123", "name": "terminal", "content": "output here"}</tool_response>-
如果 tool content 看起来像 JSON(以
{或[开头),它会被解析,因此content字段会包含 JSON object / array,而不是 string -
多个 tool results 会在一个 message 中用换行符连接
-
tool name 会根据位置与 parent assistant 的
tool_callsarray 匹配
System Message
Section titled “System Message”system message 会在保存时生成(不是从 conversation 中获取)。它遵循 Hermes function-calling prompt template,包含:
- 解释 function-calling protocol 的 preamble
- 包含 JSON tool definitions 的
<tools>XML block FunctionCallobjects 的 schema reference<tool_call>example
Tool definitions 包含 name、description、parameters 和 required(设置为 null 以匹配 canonical format)。
Loading Trajectories
Section titled “Loading Trajectories”Trajectories 是标准 JSONL —— 可以使用任何 JSON-lines reader 加载:
import json
def load_trajectories(path: str): """从 JSONL 文件加载 trajectory entries。""" 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 的 conversationstraining_data = [e["conversations"] for e in successful]Loading for HuggingFace Datasets
Section titled “Loading for HuggingFace Datasets”from datasets import load_dataset
ds = load_dataset("json", data_files="trajectory_samples.jsonl")规范化后的 tool_stats schema 确保所有 entries 都有相同 columns,防止 dataset loading 期间出现 Arrow schema mismatch errors。
Controlling Trajectory Saving
Section titled “Controlling Trajectory Saving”在 CLI 中,trajectory saving 由以下内容控制:
agent: save_trajectories: true # 默认:false或者通过 --save-trajectories flag 控制。当 agent 使用 save_trajectories=True 初始化时,_save_trajectory() 方法会在每个 conversation turn 结束时调用。
batch runner 总是会保存 trajectories(这是它的主要目的)。
batch runner 会自动丢弃所有 turns 都没有 reasoning 的 samples,以避免用 non-reasoning examples 污染 training data。