OpenAI 格式只是 sharegpt 的特例:`tags` 机制怎么做到的

2026-08-09

本文事实以 hiyouga/LlamaFactory 官方仓库 2026-08-09 的 data/README.mddata/dataset_info.json 为准。我们没有安装、训练或部署过任何模型,文中出现的字段名与默认值都是仓库文档里写着的值。

data/README.md 里有一句很容易被划过去的定位:openai 格式只是 sharegpt 格式的一个特例,其中第一条消息可以是 system prompt。这句话的实际分量在于——formatting 这个字段压根就没有 openai 这个取值。

会撞上这件事的人通常是同一类处境:手上已经攒了一批 OpenAI 风格的对话数据,每条是一个 messages 数组,元素形如 {"role": ..., "content": ...}。想拿去做微调,第一反应无非两条——写个转换脚本把它改成别的形状,或者在 dataset_info.json 里把 formatting 填成 openai。两条都是白费力气。真正要做的只有一件事:写对一段 dataset description。

先把 formatting 的取值范围钉死

dataset_info.json 的字段规格里,formatting 那一行的原文是:

the format of the dataset. (optional, default: alpaca, can be chosen from {alpaca, sharegpt})

三个信息,一个都别漏。第一,它是可选的;第二,不写的时候默认按 alpaca 读——所以一个只填了 file_name 的自定义数据集会被当成 alpaca 格式处理;第三,合法取值只有 alpacasharegpt 两个。data/README.md 全文也只有 ## Alpaca Format## Sharegpt Format 两个顶级格式章节,### OpenAI Format 是挂在后者下面的一个小节。

对应地,README 给出的 openai 格式那份 dataset description 里,formatting 填的仍然是 sharegpt。这不是笔误,正是”特例”这两个字的直接体现。

把两段 JSON 摆在一起,差异就是全部答案

sharegpt 格式的监督微调数据,原文示例长这样:

[
  {
    "conversations": [
      { "from": "human",         "value": "user instruction" },
      { "from": "function_call", "value": "tool arguments" },
      { "from": "observation",   "value": "tool result" },
      { "from": "gpt",           "value": "model response" }
    ],
    "system": "system prompt (optional)",
    "tools": "tool description (optional)"
  }
]

openai 格式那一节给的是这样:

[
  {
    "messages": [
      { "role": "system",    "content": "system prompt (optional)" },
      { "role": "user",      "content": "user instruction" },
      { "role": "assistant", "content": "model response" }
    ]
  }
]

结构完全同构:外层一个列名装着消息列表,列表里每个元素两个键,一个放身份、一个放内容。变的只有名字——列名从 conversations 变成 messages,键名从 from/value 变成 role/content,身份取值从 human/gpt 变成 user/assistant

既然只是名字变了,那么让框架认得它,需要的就不是新格式,而是一张改名表。这张表就是 tags

tags 的七个字段,其实分成两层

dataset_info.json 的规格里,tags 一段有一句限定说明:used for the sharegpt format。也就是说这段配置只对 sharegpt 生效,alpaca 那边写了也没有意义。

七个字段容易被当成一串并列的东西背下来,但它们其实分两层,分清了就再也不会填错:

第一层是”去哪个键里取”——role_tag(默认 from)指明消息对象里哪个键代表身份,content_tag(默认 value)指明哪个键代表内容。

第二层是”取出来的身份字符串等于什么算什么”——user_tag(默认 human)、assistant_tag(默认 gpt)、observation_tag(默认 observation)、function_tag(默认 function_call)、system_tag(默认 system)。这五个描述的是 role_tag 那个键上的取值,不是键名本身。

理解了这个两层结构,README 给的那份 openai dataset description 就是一目了然的:

"dataset_name": {
  "file_name": "data.json",
  "formatting": "sharegpt",
  "columns": {
    "messages": "messages"
  },
  "tags": {
    "role_tag": "role",
    "content_tag": "content",
    "user_tag": "user",
    "assistant_tag": "assistant",
    "system_tag": "system"
  }
}

逐格对照一下:

tags 字段sharegpt 默认值官方给的 openai description
role_tagfromrole
content_tagvaluecontent
user_taghumanuser
assistant_taggptassistant
observation_tagobservation未列出
function_tagfunction_call未列出
system_tagsystemsystem

两处值得多看一眼。一是 system_tag:它的默认值本来就是 system,openai 那份仍然把它显式写了一遍,等于把这一格照着默认值又抄了一次。二是 observation_tagfunction_tag 在这份示例里没有出现——规格给这两个字段各自标了默认值,示例没写就是维持规格里的默认;官方在这一节没有多解释,我们也不替它延伸。

别只改一半:columns 管的是另一件事

最容易漏的一步不在 tags 里,而在它上面那段 columns

tags 管的是”消息对象内部长什么样”,columns 管的是”哪一列装着这个消息列表”。规格里 columns.messages 的原文默认值是 conversations——也就是说,不写这一格,框架会去找名叫 conversations 的列。而 OpenAI 风格的数据,那一列名叫 messages

所以官方那份 description 里 "messages": "messages" 这一行看着像废话,其实是必需的:左边是 LlamaFactory 的字段名,右边是你数据里的真实列名,两边碰巧同名而已。只改 tags 不改 columns,或者只改 columns 不改 tags,都属于改了一半。

由此得到的一般结论

把上面两段 JSON 直接对比,能得出一个比”支持 openai 格式”更有用的结论:tags 让 sharegpt 格式可以适配任意字段命名的对话数据。

openai 格式不是被特别实现出来的一种格式,它只是一组特定的 tags 取值。你手上的数据如果既不是 from/value,也不是 role/content,而是自家系统导出的别的键名,那么按同样的思路把 tags 里对应的字段改成你的键名和取值就行——这是规格本身给出的自由度。

需要克制的地方也在这里:具体到你的数据能不能一次跑通、要不要额外清洗,取决于你的数据本身,官方没有给通用的清单或建议值,我们也没有跑过任何一次训练,不替你下结论。

三条边界,别自己脑补

第一,位置约束这件事,原文放在别处。 sharegpt 的监督微调一节写明了一条硬规则:human 与 observation 必须出现在奇数位置,gpt 与 function 必须出现在偶数位置;同一节还写了 gpt 和 function 会被模型学习。而 ### OpenAI Format 一节的原文,除了那句”是 sharegpt 的特例”,只额外补了一句”第一条消息可以是 system prompt”,并没有把位置约束重述一遍。这条约束在 openai 形状的数据上具体怎么落,原文没写,我们不推断。关于位置约束本身我们另有一篇专门讲。

第二,预训练这条路在 sharegpt 这一支是堵着的。 data/README.md 的 Sharegpt Format 一节下面,### Pre-training Dataset 的正文只有一句:Not yet supported, please use the alpaca format.(尚不支持,请使用 alpaca 格式)。这是一处写得很干脆的能力边界,原文自己也给了指路方向。

第三,system 有两条并行的路径。 一条是 columns.system(默认 None),一条是 tags.system_tag(默认 system)。规格在 system_tag 这一条后面特别注了一句 can override system column。官方那份 openai description 只写了 system_tag、没有写 columns.system。这两处的关系原文就给到这一句,我们照录,不做扩展。

落地时的两件杂事

一件是位置。规格原文写死了两条:dataset_info.json 必须放在 dataset_dir 目录下,dataset_dir 的默认值是 ./datahparams/data_args.pydataset_dir 的默认值也是 "data");使用自定义数据集时必须dataset_info.json 里加一段 dataset description,并在训练前指定 dataset: dataset_name。允许的文件类型是 json、jsonl、csv、parquet、arrow 五种。

另一件是路径写法。训练配置里指向数据目录、并按名字点用数据集,两行就够:

dataset_dir: <你的项目目录>/data
dataset: dataset_name

Linux / macOS 侧保持默认的 ./data 或写这样一个路径都可以。Windows 侧要多留一句心:如果你把数据放在项目外,路径里的反斜杠在 YAML / JSON 文本里容易踩到转义,通用做法是统一写成正斜杠。这一条是通用的文本格式常识,不是 LlamaFactory 官方文档里的内容,写在这里只是提醒别在这种地方浪费时间。以上片段为按官方字段语义组合的示例,未逐项实测,以官方文档与 llamafactory-cli train -h 的实际输出为准。

想照着官方样例对一遍,去哪儿看

写完配置最稳的自查方式,是拿官方自带的样例数据比对形状。data/README.md 在各节里点名的样例文件是分开的:alpaca 监督微调那一节给的是 alpaca_en_demo.json,sharegpt 监督微调那一节给的是 glaive_toolcall_en_demo.json,sharegpt 的偏好数据那一节给的是 dpo_en_demo.json。我们实读 data/dataset_info.json,里面共有 104 个数据集条目,前面这几个 demo 名字就在开头一段里,identityalpaca_en_demoalpaca_zh_demoglaive_toolcall_en_demoglaive_toolcall_zh_demo 依次排着——也就是说,样例数据本身就是按同一套 description 规格注册进去的,照着改比凭空写更省事。

顺带一个容易被忽略的默认值:ranking 默认是 False,偏好数据集要显式写成 true。官方那份 openai description 里没有 ranking 这一行,也就是维持默认、走监督微调这条路。至于 OpenAI 风格的偏好数据该怎么配,原文在 ### OpenAI Format 一节里没有交代,我们不推断。

一句话收口

如果你的对话数据本来就是 conversations 列 + from/value 键 + human/gpt 取值,那 tags 整段可以不写,默认值就是对的;只要有任何一处键名或取值不同——OpenAI 风格的 messages/role/content/user/assistant 就是最典型的那一种——就得把 columnstags 两段一起补上。写转换脚本不是必经之路,官方给的路是写配置。


本文依据 LlamaFactory 官方仓库(github.com/hiyouga/LlamaFactory)的 README、data/README.mdexamples/ 下的配置与 src/llamafactory/hparams/ 的参数定义整理,核对日 2026-08-09。本文内容为仓库源码与文档口径,我们没有安装、训练或部署过任何模型,文中显存数字均为官方标注的估算值(README 原文标 * estimated)而非实测占用。参数与默认值随版本变动,请以 llamafactory-cli train -h 的实际输出为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。