news 2026/10/3 1:58:40

Operit Markdown 聊天记录导入导出格式完全指南:基于 HTML 注释的对话交换协议

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Operit Markdown 聊天记录导入导出格式完全指南:基于 HTML 注释的对话交换协议
  • AI Agent
  • 人工智能
  • 大模型
  • AI 应用
  • 工具调用
  • 本地部署
  • MCP Clients
  • Agent 记忆

【免费下载链接】Operit

The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent

项目地址:https://gitcode.com/gh_mirrors/op/Operit
点击查看免费下载

本文档系统讲解 Operit(Android 上的 AI Agent 与 AI 聊天应用)所采用的 Markdown 聊天记录导入/导出格式。该格式以简洁的 HTML 注释承载元数据,让用户无需编写复杂 JSON 即可手工编写对话记录,并支持通过 Zip 压缩包批量迁移多个对话。读完本文,你将掌握 chat-info / msg 注释的完整语法、默认值规则、格式自动检测逻辑,以及导入导出在源码层的实现原理,能够自行编写、校验并批量迁移 Operit 聊天记录。

为什么选择「HTML 注释 + Markdown」格式

在导入/导出聊天记录的场景中,最常见的做法是导出为结构化 JSON。但 Operit 面向的是「人可以直接阅读、甚至可以手工编写」的交换格式,因此采用了基于 HTML 注释的简洁解析模式。

  • 简单易写:使用key=value键值对注释存储元数据,无需编写复杂 JSON;
  • 大量缺省:只需指定必要信息,其余字段自动生成;
  • 精准分割:基于注释分割消息,消息内容纯净,不受正文干扰;
  • 批量支持:通过 Zip 压缩包支持多对话一次性导入/导出。

该设计理念在格式协议文档中定义为“新版 Markdown 格式聊天记录导入规范”,并在导入示例文件中给出了可直接使用的完整样例。

关键约定:每个 Markdown 文件对应一个独立对话。不再支持在单个文件中使用---分割多个对话;---出现时会被视为正文内容的一部分。

单文件基本结构

一个合法的导入文件包含三个部分:chat-info全局元数据注释、若干条msg消息注释、以及紧随其后的消息正文。以下示例取自 chat_import_markdown_example.md:

<!-- chat-info: title=Python 编程讨论, group=编程技术 --> # Python 编程讨论 <!-- msg: user --> ## 👤 User 如何在 Python 中读取 JSON 文件? <!-- msg: ai, model=gpt-4 --> ## 🤖 Assistant *Model: gpt-4* 在 Python 中读取 JSON 文件很简单,可以使用内置的 `json` 模块: ```python import json # 方法1:读取 JSON 文件 with open('data.json', 'r', encoding='utf-8') as f: data = json.load(f)

关键要点:

  • json.load()用于从文件对象读取
  • json.loads()用于从字符串读取

👤 User

如果 JSON 文件很大怎么办?

可以看出:文件开头可选的 `# 标题`、每条消息前的 `## 👤 User` / `## 🤖 Assistant` 装饰标题,在导入时都会被自动忽略或按装饰处理,真正驱动解析的是注释标记。 ## chat-info:全局元数据注释 文件头部包含 `chat-info` 注释,用于描述整个对话的元信息。 **格式**: ```html <!-- chat-info: key=value, key=value -->

支持字段(均可选):

字段说明缺省值
title对话标题"Imported from Markdown"
created创建时间当前导入时间
id唯一标识符自动生成 UUID
group分组名称"从 Markdown 导入"

示例:

<!-- chat-info: title=Python 学习笔记, group=编程 -->

在源码 MarkdownConverter.kt 中,chat-info的解析逻辑位于主解析循环开头:命中<!-- chat-info:前缀后调用parseSimpleProperties提取键值对,其中title与created会被实际消费,分别覆盖标题与创建时间;created字符串通过parseDate按多种格式尝试解析(如ISO_LOCAL_DATE_TIME、yyyy-MM-dd HH:mm:ss等,见dateFormatters列表)。其余字段如id、group在导入端并不覆盖内部值——id一律重新生成UUID.randomUUID(),group统一取本地化字符串markdown_import_from(即“从 Markdown 导入”)。

msg:消息元数据注释

每条消息以msg注释开头,声明角色及可选的模型、时间戳信息。

格式:

<!-- msg: role, key=value... -->

支持简写:可以直接写角色名,无需role=前缀。

  • <!-- msg: user -->等同于<!-- msg: role=user -->
  • <!-- msg: ai -->等同于<!-- msg: role=ai -->

支持字段:

字段说明缺省值
role角色 (user/ai)user
model模型名称markdown
timestamp时间戳自动顺序生成

示例:

<!-- msg: user --> <!-- msg: ai, model=gpt-4 --> <!-- msg: user, timestamp=1700000000000 -->

源码层面的解析细节(MarkdownConverter.kt):

  • 属性按逗号或分号分割,key=value形式存入映射;没有等号的项(如简写角色user)整体作为 key、值为空字符串存入——这为角色简写提供了统一的数据结构基础;
  • 角色识别由parseRole完成:除user/ai/assistant英文关键字外,还支持本地化字符串(如中文“用户/助手/系统/模型”,取自R.string.message_role_user、R.string.role_assistant等),并且会先剔除 emoji 与特殊符号再比对;system被映射为user,model被映射为ai;无法识别时返回null,此时才回退到默认角色user;
  • model缺省为字符串markdown;
  • timestamp缺省时按baseTimestamp + messageIndex * 100L自动顺序生成,保证消息时间戳单调递增;显式给出则直接使用。

消息内容与装饰标题处理

紧跟在msg注释后的内容即为消息正文。若注释后紧接着出现## User或## Assistant形式的标题,导入时会自动将该行忽略(作为视觉装饰)。

源码中通过justStartedMessage标志实现该逻辑(MarkdownConverter.kt):在刚解析完一条msg注释后,若遇到以##开头的行,且该行经parseRole可识别为角色文本,则跳过;注释后的空行同样被跳过;此后所有非注释行按行追加进当前消息内容,直至下一条msg注释出现。

也就是说,正文中的任何 Markdown 语法(代码块、列表、加粗、嵌套标题等)都会原样保留,导入端不会做内容改写,消息内容“纯净”地取自注释之间的区间。

格式自动检测:如何识别 Markdown 导入文件

Operit 的导入流程并不要求用户手动声明格式,而是由 ChatFormatDetector.kt 自动判定:

  • 强匹配:内容中任意一行以<!-- chat-info:或<!-- msg:开头,直接判定为 Markdown 格式;
  • 弱匹配:同时满足「存在#开头的 Markdown 标题」与「存在## User|Assistant|AI|System|Model|用户|助手|系统|模型这类整行对话标记」时,也判定为 Markdown;
  • 检测顺序为 Markdown → CSV → JSON(含 ChatGPT / Operit / Claude / 通用 JSON 细分)→ 纯文本,见detectFormat与detectFormatByExtension(.md/.markdown扩展名直接映射为 MARKDOWN)。

导入端的分发逻辑位于 ChatHistoryManager.kt,检测结果为ChatFormat.MARKDOWN时实例化MarkdownConverter(context)执行转换;转换失败会抛出携带本地化提示文案的ConversionException。

导出实现:导入格式的镜像

Operit 导出 Markdown 时(MarkdownExporter.kt)生成的产物与导入规范完全互操作:

  • 头部写入一行chat-info,依次携带id、title、created、updated与可选的group;
  • 同时保留一段 YAML Front Matter(title/created/updated/group/messages计数)用于兼容旧版解析器与提升可读性——导入端 MarkdownConverter.kt 也确实兼容解析旧版 YAML Front Matter;
  • 每条消息输出<!-- msg: role, model=..., timestamp=... -->(角色用简写形式,模型非markdown时才写出,时间戳总是写出);
  • 随后附上## 👤 User/## 🤖 Assistant装饰标题与*Model: xxx*视觉元信息,便于人类阅读;
  • 消息正文原样写出。

由此,导出文件可直接作为导入文件使用,形成闭合的“导出 → 手工编辑 → 再导入”工作流。

Zip 批量导入/导出

当对话数量较多时,Operit 通过 Zip 压缩包统一管理。

导出:选择 Markdown 导出多对话时,系统生成chat_backup_<时间戳>.zip。包内每个对话对应一个.md文件,文件名为对话标题;非法文件名字符(\ / : * ? " < > |)会被替换为下划线,重名文件自动追加(1)、(2)等序号避免覆盖(见 ChatHistoryManager.kt,使用ZipOutputStream逐条写入)。

导入:将多个符合规范的.md文件打包为.zip后一次性导入,系统使用ZipInputStream自动解压并逐个读取包内所有.md文件,每个文件被导入为一个独立对话。

常见问题

Q: 可以在一个文件中混合不同的对话吗?

A:不再支持。每个对话必须保存为单独的.md文件;在单个文件中使用---分隔会被当作正文内容的一部分。源码 MarkdownConverter.kt 中splitConversations直接返回整个文件内容,注释明确说明“不再支持通过---分割对话,整个文件视为一个对话”。

Q: 我可以手动编写这种格式吗?

A:可以。只需确保文件扩展名为.md,且包含<!-- msg: ... -->注释即可。其余字段均可省略,靠缺省值兜底。

Q: 为什么我的文件无法导入?

A:请依次检查:

  1. 文件是否包含<!-- msg: ... -->注释(这是格式检测与消息解析的前提);
  2. 如果是 Zip 包,请确保包内是.md文件而不是嵌套压缩包或其他格式;
  3. 确认没有在单个文件中用---尝试分割多个对话。

兼容性与边界说明

  • 旧版兼容:导入端仍支持 YAML Front Matter 头部(title:、created:键值),可平滑迁移旧格式文件;
  • 角色映射:system角色在导入时被映射为user,model被映射为ai,说明当前对话模型以双角色(user/ai)为主;
  • 本地化:角色识别与导入分组的文案均走资源字符串(values/strings.xml及values-en、values-ro等多语言副本),意味着非英文角色名也可被识别;
  • 格式边界:ChatFormat枚举中还包含 OPERIT(原生 JSON)、CHATGPT、CHATBOX、CLAUDE、GENERIC_JSON、CSV、PLAIN_TEXT 等格式(见 ChatFormat.kt),Markdown 只是导入生态中的一环;Claude 格式目前回退到通用 JSON 转换器处理。

掌握以上规范后,你既可以手工编写或程序化生成 Operit 可识别的 Markdown 对话文件,也能理解导出产物的每个字段来源,实现跨设备、跨工具的聊天记录无损迁移。

  • AI Agent
  • 人工智能
  • 大模型
  • AI 应用
  • 工具调用
  • 本地部署
  • MCP Clients
  • Agent 记忆

【免费下载链接】Operit

The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent

项目地址:https://gitcode.com/gh_mirrors/op/Operit
点击查看免费下载

相关推荐

上一篇:Chat LangChain 生产环境上线清单
下一篇:终极指南:如何快速掌握Clean Code PHP编码规范提升团队协作效率

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 1:58:39

STM32F1自定义HID实战:寄存器级USB协议栈开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华