← 返回列表
需源码安装
GEML — 通用表达标记语言
暂不能直接安装(需源码编译或环境不满足):仓库缺少 package.json,无法用 dsh 插件安装命令安装。 · 最近上游提交 2026/9/17 · 已提供中文文档
一种格式,两种读者。人和 AI 智能体如今共同撰写同一份文档。对人而言清晰易读;对机器而言可寻址、可验证、可版本化。GEML 是纯文本——通过一个类型化块来组织一切,并由一个 .gemlhistory 附属文件来记录。
综合分
44.4
GitHub 分
44.4
用户评分
—
★ Stars
26
周下载量
—
兼容 / 相关生态插件(非 dsh 原生,请按其对应运行时安装)
git clone https://github.com/geml-spec/geml.git数据截至 2026/9/17(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查需源码安装
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包geml(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
仓库缺少 package.json,无法用 dsh 插件安装命令安装
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/17 23:37:45
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
MCP Toplist Mentioned in Awesome
GEML — 通用表达标记语言
npm MCP CI GEML check spec: 1.0 code: MIT spec license: CC BY 4.0
一种格式,两类读者。
在智能体驱动的开发与知识工作中,纯文本和 Markdown 没有确定性的块边界:程序和模型只能把整个文件送进去、再把整个文件拿回来——最多用行窗口去试探,并逐字重述原文来改写它。Token 成本随文档长度增长,操作也变得臃肿。经过几轮重写后,在别处摘录的副本就开始漂移。
你可以什么都不改就开始。 geml list、geml find 和 geml get 直接作用于你已有的 Markdown——不转换任何内容,不产生新文件,你的 .md 依然是 .md:
geml list README.md # 每个章节,作为一个地址
geml get README.md '#key-features' # 只读一个章节,而不是整个文件
geml set README.md '#key-features' --body # 把一个章节写回去
geml replace README.md 'old text' 'new text' # 替换一个字符串,并告知它位于哪个块
只有那个章节进入智能体的上下文——几 KB,而不是整个约 48 KB 的文件。
需要比章节更细——一个块、一张图表、一张表格?让 .geml 站在中间地带:以那种粒度编辑,而你交付的 --to md 永远不会与它漂移。
块有名字;其中的东西有坐标。 表格的单元格、data 块的叶子、meta 中的键——每一个都有结构本身赋予它的坐标,而 get 和 set 恰好落在那个值上。
geml get doc.geml '#fy[2]["Q1"]' # 一个单元格
geml set doc.geml '#intake["fields"][1]["name"]' # JSON 中的一个叶子节点
对人来说,它是读起来干净利落的纯文本;对智能体来说,它是可寻址、可验证、可追溯、可回退的 "Doc-as-a-Base"。
GEML 是极简的。
它是纯文本——即使没有渲染器也依然干净;
整个语言只有一种块语法;
原生地具备可寻址、可验证、可引用的结构。
GEML 不为每种内容单独发明一套迷你语法,而是把所有类型都装进同一个容器:带类型的块。代码是一个块。表格、图表、数学公式、标注,甚至元数据也都是——而一段散文也可以是一个块(=== text),只要你想让它可寻址。之后扩展它也同样朴素。每次的形状都一样,这让这门语言容易学到几乎不会用错。
=== code {#hello lang=python}
print("hi")
===
geml get doc.geml '#hello' # 按名称,只取这个块
块有名字,所以那些动词才有落脚之处——完整语法见
一分钟了解该格式。
目录: 它解决什么 · 为什么是现在 · 有何不同 ·
一分钟了解该格式 · 给程序员的礼物 ·
上手实践 · 配合 LLM ·
成熟度与版本 · 设计 · 路线图 · 参与其中 ·
许可证
它解决什么
解决的问题
1. 上下文负载与 token 膨胀
* 现状:JSON/XML 等数据格式带有沉重的包装标签和语法符号;Markdown 缺乏严格的结构化元数据和引用机制。
* 做法:调校标记密度和语法开销,只读写目标块——上下文成本不再随文档长度增长,让智能体的读写保持轻量。
2. AST 级精度与解析确定性
* 现状:非结构化文本在 LLM 多轮读写中逐渐劣化——格式损坏、语义漂移、解析幻觉。
* 做法:一种直接映射到抽象语法树(AST)的确定性语法,让程序和 LLM 执行原子级的块级创建/读取/更新/删除。
3. 文档副本碎片化
* 现状:多智能体协作和共享流水线通过复制粘贴传递内容,留下多个互不相连的副本。
* 做法:设计上就是单一事实来源——标准化的模块引用和数据绑定消除冗余副本和版本分歧。
关键特性
1. AST 级结构化操作
* 统一的节点定义;文档直接解析为带类型的文档树(AST)。
* Agent 精确定位目标区块、属性或组件;局部补丁与幂等更新取代整文件重写。写入以字节拼接方式落地,并伴随整文档重新校验——树服务于读取与校验,每一个未被触碰的字节都保证不变。
2. 低 token 读写
* 节省的不是标记字符——而是从未被读取的部分:#id 命中一个语义完整的区块,其余部分从不进入上下文。
* 对于相同语义,显著降低提示 token 成本:更好的模型吞吐量,更低的推理成本。
3. 单一事实来源,模块化引用
* 原生跨文档、跨片段组件引用。
* 源节点改动一次,所有引用随之更新——没有版本偏差。
4. 稳健的双向读写
* 整个语言只有一种区块形态——易于生成且难以出错,与主流 LLM 输出分布高度契合。
* 严格的校验器,提供精确的错误位置和可操作的修复反馈。
对比
| 维度 | Markdown | JSON / YAML | GEML |
| :--- | :--- | :--- | :--- |
| 上下文成本(按块 I/O) | 高(整文件进出) | 高(整文件 + 语法噪声) | 极低(仅目标区块) |
| 精确 AST 操作 | 弱(无严格语义节点) | 强 | 强(为 Agent 读写而生) |
| 人类可读性 | 高 | 中 | 高 |
| 单一来源引用 | 不支持 | 需要协议扩展 | 原生(模块化嵌入) |
| 写入安全性 | 弱 | 中 | 强(错误写入在落地前被拒绝 + 单区块回滚) |
为什么 LLM 时代需要一种全新的文本格式
因为文档的生产者和消费者都已经改变了。
在传统软件工程中,文档要么是供人阅读的静态说明,要么是供程序使用的序列化数据文件。
如今,人和 AI Agent 高频协作于同一份文档。当 Agent 成为文档的“第二读者与共同作者”时,旧的平衡被彻底打破:
1. 上下文是稀缺算力:每一次整文档读写都在消耗 Agent 有限的注意力窗口和推理预算;
2. 人机协作需要同构载体:人需要一眼读懂,Agent 需要逐块精确读写;
3. 知识必须有单一事实来源:散落的提示词和复制粘贴的 Markdown 注定在每一次迭代中衰减。
然而,我们现有的文本基础设施都不是为这一场景设计的:
* Markdown(为人排版):没有稳定的结构区块,没有机器键。要改一个参数,Agent 必须读写整段文本——在多轮循环中浪费上下文预算,并招致格式与语义的双重漂移。
* JSON / XML(为机器序列化):充斥着包装语法和结构噪声——阻碍人类自然阅读,同时在长上下文中悄悄消耗昂贵的 token。
* 草稿记忆与散落的文件(没有单一事实来源):上下文被撕裂在聊天记录和无处不在的 Markdown 副本之间;副本一经产生便已漂移,版本错位与幻觉失真随之而来。
这三种失败共同的根源,恰恰是每种工具自身的优点:Markdown 的“永不报错,什么都能写”赋予了人们书写的自由——也正因如此,机器无法信任它读回的结构;JSON/XML 的严格 schema 赋予了机器确定性——也正因如此,没有人用它来写散文。优点即缺陷,这正是补丁无法修复此问题的原因:把“损坏的引用必须让构建失败”硬塞进 Markdown,是对其契约的背叛;而剥离 JSON 的包装语法,则是对其本质的否定。当人与智能体开始高频共同书写同一份文本时,所需要的不是两极之间的妥协,而是一种从第一天起就把“人可读”与“机器可操作”视为同一项设计约束的格式。
答案:"Doc-as-a-Base"
GEML 并未发明笨重的新运行时。它借鉴 Roy Fielding 博士论文中的 REST 架构风格,为纯文本文档赋予一套标准的操作语义:
| 旧痛点 | 对应能力(四大法则) | 为开发者和智能体带来什么 |
| :--- | :--- | :--- |
| 改一处就要重写全文 | 寻址法则 | 每个块都带有 #id;get/set 只读写该块。从未加载的东西不可能被破坏——上下文窗口始终属于你。 |
| 副本无处不在,全都在漂移 | 投影法则 | === embed 动态求值,而非复制粘贴;在源头定义一次,便终结了同步副本的劳作。 |
| 坏格式 / 损坏引用污染下游 | 校验法则 | 引用与语法在构建时检查;坏写入在落地之前就被拦截,无需等待人工审查。 |
| 一次坏编辑迫使整文件回滚 | 回滚法则 | 配套的 .gemlhistory 可原子性地回滚单个块——无需拆掉整个页面;为智能体提供轻量级版本安全网。 |
文档不再只需要一种格式——它需要一组动词。 GEML 保留纯文本的可读性,并加入确定性的块级操作。
💡 深入探讨:
如果你对 LLM 时代工程文档的困境,以及为什么我们需要从头重新设计一种纯文本格式感兴趣,请阅读我们博客上的完整文章:“为什么在 LLM 时代我们需要一种新的文本格式?”
GEML 有何不同
GEML 有意保持小巧——其中的思考、它拒绝什么,以及哪些仍然开放,都在我们如何思考设计中。
这四项能力在上一章已经确立——寻址、投影、验证、回滚。本章讲的是每种格式相对于这些能力落在何处,以及 GEML 在哪里划定自己的边界。
其他格式如何比较
这四项能力在各自领域都有成熟的解决方案;不寻常的是在一种纯文本格式中同时满足全部四项:
| 家族 | 状态实际上是什么 | 可寻址 / 可引用 | 可投影 / 可嵌入 | 可验证 | 历史 / 可追溯性 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| Word / Docs | 不透明状态 | ❌ 没有块级键;通过平台 API 访问 | ❌ 只能复制粘贴 | ❌ 完全没有检查 | ⚠️ 平台服务器端,不在文件中 |
| Markdown / AsciiDoc | 字符流 | ⚠️ 标题锚点或方言 id;没有读/写动词 | ⚠️ 方言嵌入(Obsidian ![[…]]、include::)——会静默失效 | ❌ 断链会静默失败 | ❌ 格式内没有——需要外部 git |
| JSON / XML | 数据序列化 | ✔️(id / schema) | ⚠️ 仅 XML(XInclude,外部) | ✔️ 通过外部工具链 | ❌ 格式内没有——需要外部 git |
| GEML | 纯文本 + 块结构 | ✔️ 每个块一个唯一的 #id(原生可引用) | ✔️ === embed:引用即查找(原生) | ✔️ 构建时错误 | ✔️ 文件旁的 .gemlhistory(原生可追溯) |
逐项对比:vs. CommonMark · vs. XML 和 JSON · 7 种格式能力矩阵。
与 Markdown 共存:GEML 是编辑的事实来源,Markdown 是交付产物。用 geml --to md|html 单向投影,并像以前一样交付 .md 或 .html。协作,而非锁定。 (投影是有损的:块 id 和表绑定图表无法在其中保留。)
别只听表格怎么说——重新跑一遍。 这是我问模型的问题:
根据你刚才编辑 README 的亲身经验,描述你在文档上经历的命令步骤(我看到你在用 grep 之类的东西),以及你是否会缓存文档以节省 token——我们来比较一下,并由此看看 GEML 的哪些部分真正值得拥有自己的位置。
返回的结果是:一次编辑的成本 和 真实一天的重放。把这个问题粘贴给你自己的模型,看看它会告诉你什么。
附注:我仍在尝试弄清 codemap 生成的上游链(谁调用它)和下游链(它调用了什么)是否能以同样的方式确定函数和调用点——并修改项目代码。等我有了报告,我会发布一份。
一分钟了解该格式
类型化块
一种形态,适用于所有类型。 块的基本语法是 === type [attributes] … ===(其中 {#id .class key=val} 之类的属性是可选的)——只有 type(以及其正文的读取方式)会发生变化:
=== code {lang=python}
print("hi")
===
=== note {.intro}
Parsed prose with emphasis and a [[#budget]] reference.
===
=== meta
title = "Budget plan"
===
一连串 =(三个或更多)开启一个块;等长的 = 序列将其关闭;更长的围栏嵌套在较短的围栏之内。带有 #id 的块也可以用带标签的围栏 === #id 来关闭——无需数围栏长度,这使得长块更不容易出错(嵌套仍然需要更长的外层围栏:正文中同长度的裸 === 会提前关闭块,无论是否带标签)。类型决定正文的读取方式——raw(逐字:code、diagram、math、table)、flow(带内联标记的解析散文:note、text)或 data(每行一个 key=val:meta);embed 完全不携带正文——它的 src= 指明它所代表的块——并且每个块都可以携带一个属性对象 {#id .class key=val},其中 .class 是语义标签,绝不是样式钩子。完整的内联语法(强调、链接、[[#id]] 自动引用、媒体、脚注、内联 $math$)见规范。
表格——两种正文,一个模型
以可视化方式编写表格:
=== table {#budget caption="Annual cost"}
| Plan | Months | Rate |
|-------|-------:|-----:|
| Basic | 1 | 30 |
| Pro | 2 | 30 |
===
……或作为数据。表格保存事实;其上的 view 派生出计算列和汇总行:
=== table {#fy25 format=csv header=1}
Segment, Q1, Q2, Q3, Q4
Cloud, 8, 10, 12, 14
Platform, 5, 6, 7, 9
Services, 3, 4, 4, 5
===
=== view {#fy25-report src=#fy25 compute="FY [%.1f] = Q1 + Q2 + Q3 + Q4; n = 1" summary="Segment = 'Total'; FY [%.1f] = sum(FY); n = sum(n)"}
===
两种表格形式描述的是同一个模型。FY 列和 Total 行由视图在构建时计算:
| Segment | Q1 | Q2 | Q3 | Q4 | FY | n |
|-----------|---:|---:|---:|---:|-----:|--:|
| Cloud | 8 | 10 | 12 | 14 | 44.0 | 1 |
| Platform | 5 | 6 | 7 | 9 | 27.0 | 1 |
| Services | 3 | 4 | 4 | 5 | 16.0 | 1 |
| Total | | | | | 87.0 | 3 |
compute 对每行按列执行 + - * / ( );summary 根据聚合值 sum / avg / min / max / count 添加一个脚行(可对它们进行算术运算,例如加权比率);末尾的 [printf] 设置数字显示格式。上面的 n 是行数惯用法——count 统计某一列中的非空单元格,因此对一个常量列求和就是统计行数。
表格还可以通过 src="regions.csv" 从外部 CSV 拉取数据。
❓ 有待讨论: 计算列和汇总行应该保留吗?保留、冻结还是删除——请说明。
数学
=== math {#gauss caption="Gaussian integral"}
\int_{-\infty}^ e^{-x^2} dx = \sqrt
===
$$\int_{-\infty}^ e^{-x^2} dx = \sqrt$$
图表与图形——托管 DSL,还是为表格绘图
GEML 从不解释图表主体;它将其路由到可插拔的渲染器(未知的 format 是警告,主体保留):
=== diagram {#flow format=mermaid caption="Review flow"}
graph LR
A[Draft] --> B{Review} -->|ok| C[Publish]
===
graph LR
A[Draft] --> B{Review} -->|ok| C[Publish]
图表也可以为表格绘图——单一事实来源,列引用在构建时检查,且不复制数据:
=== diagram {format=geml-chart data=#fy25-report type=bar x=Segment y=FY}
===
根据上面的 #fy25-report 视图绘制——FY 是计算列,因此图表绑定到派生它的视图,而不是基础表:
xychart-beta
title "FY by segment"
x-axis [Cloud, Platform, Services]
y-axis "FY"
bar [44, 27, 16]
数据——一个值,而不仅仅是文本
每种块类型都指明其内容:code 是代码区域,table 是网格,math 是公式。data 持有数据值,数据格式就在这里——json(默认)、jsonl 和 yaml 用于声明的子集;toml 保留。由于是类型化的,主体会被读取,而不仅仅是显示:缺少逗号会导致构建失败,geml get --json 返回该值本身,图表可以直接读取它。
=== data {#log format=jsonl}
{"ts":"09:00","p95":41}
{"ts":"09:10","p95":58}
===
jsonl 主体每行一条记录,程序可以在文件末尾盲目追加。记录也可以保留在自己的文件中:src=ops/latency.jsonl#L900-999 指定文件,并可选地指定行窗口——因此日志可以像以前一样继续追加和跟踪,而文档是它的经过验证、可寻址、可绘图的视图。
嵌入——动态引用,而非副本
一个块可以代表另一个块:在同一文档中通过 src=#id,跨文档通过 src=other.geml#id。嵌入是在渲染时对源的动态查找——更改源一次,每个嵌入都会跟随;删除它,geml check 会立即导致构建失败。
=== embed {src=#fy25}
===
主体保持为空;目标位于 src= 中。
Markdown 无法向你展示投影。要实时查看:安装浏览器扩展,打开 sample.geml 的原始链接,然后滚动到 Transclusion 部分——同文档投影(src=#roadmap)、跨文档投影,甚至链式解析(一个嵌入拉取一张图表,而该图表本身又绑定到另一个文件中的表格)都会就地渲染:那里没有写入任何内容,但只需编辑一次源,投影就会随之更新。
给程序员的礼物 — geml-code-graph
为了测试 GEML 的表达能力和灵活性——最重要的是看看块级双向链接是否站得住脚——让我们在一个代码图上试试,这是程序员熟悉但要求很高的场景:
你整个代码库的调用图,用 GEML 编写。 geml codemap build 将调用图展开为 GEML 文档树——每个方法都是一个 #id 块,带有 #calls / #called-by 双向边。用于故障排查的下游链(一个方法调用了什么)、用于影响范围的上游链(谁调用了它)——全部一秒可见;
geml-parser/render.ts 的方法图:悬停 RenderCtx.inline 会点亮其整个调用者链,而其他一切都会变暗;点击节点会在图旁边打开其源代码
npm i -g @geml/geml
geml codemap build # --root defaults to . : detect languages -> index -> one merged graph in ./.geml-code-graph/
geml codemap serve # opens your browser on the graph
[!NOTE]
要求。 CLI 需要 Node 22+(npm i -g @geml/geml)。以下所有内容
都是可选的,并且仅在注明处使用:Joern
用于代码图中的非 TS/JS 语言,以及 Chrome 用于
查看器扩展。
[!TIP]
TS/JS —— 零配置:build 会自行获取 scip 索引器。
Java / C / Python / Go / Kotlin —— 额外下载一个 Joern:解压其发布包并将该文件夹传给 build,例如 --joern ~/joern/joern-cli(Windows 上为 --joern C:\joern\joern-cli),或者将其放到 PATH 中并跳过该标志。
混合前端 + 后端仓库——所有内容都会合并到一个图中。
geml-code-graph 本身就是一个图表格式——一行即可将其嵌入任何 GEML 文档(=== diagram {format=geml-code-graph src=.geml-code-graph/index.geml} ===),并且一个可选的每次提交钩子(与 Claude skill 捆绑)会在代码变动时重建它,因此图不会漂移。
规模是经过测量的,而不是承诺的:在 Apache Flink 的代码库上——13,585 个 Java 源文件、约 81,000 个方法、266,821 条调用边——纯文本数据表仍然可以即时打开和查询,而且你可以 grep 任何方法名来追踪其调用链。
自己动手复现:克隆 apache/flink,并在其根目录运行 geml codemap build --joern …。
下一步——立即动手实践
▶ 在 Playground 中尝试编写 GEML——左侧编辑,右侧实时渲染,一旦引用失效,构建判定就会立刻变红。无需安装,也无需事先阅读任何内容。
然后,按适合你的顺序:
1. 在浏览器中查看渲染效果。 安装 扩展,然后打开一个原始 .geml 链接(是原始文件,而不是 GitHub blob 页面——那个是 HTML):GEML 规范本身(吃自己的狗粮——规范本身就是一份 GEML 文档,并大规模渲染)、showcase(一个计算表格、四个图表、一个 Mermaid 流程图,以及数学公式),或者 playground/sample.geml,用于交互式代码图。
2. 查看由文档布局出的整个页面。 playground/style-demo/ 是 GitHub blob 页面的 1:1 复刻——顶栏、文件树、面包屑、Preview/Code/Blame、下拉菜单——其中 page.geml 保存所有字符串,github.style.geml 保存所有颜色和长度,而查看器对二者一无所知。它需要扩展以及本地服务器(原因,以及两条命令):该页面会获取其样式表和图标,而 raw.githubusercontent.com 禁止这样做。
3. 在本地运行。 npm i -g @geml/geml(Node 22+),然后对文档运行 geml check,或者用 geml codemap build 将其指向你自己的仓库。
4. 设置 Claude Code——一条命令。 npx -y @geml/geml skill install 会安装编写技能、CLI 和 MCP 服务器,用户全局生效,适用于每个项目。它不会编辑任何设置,也不会安装任何钩子。详情。
5. 阅读语法。 完整规范(EN / 中文)是规范性的,而且短到可以坐下来一口气读完。
6. 或者逐条规则地看它如何运作。 图解 GEML(EN / )——十一个自包含页面,每种块类型、每种 profile,以及 CLI 各一页:左侧是 GEML,右侧是处理器实际做的事情(geml check 诊断、geml list 地址、--to html 标记),每条规则都标注了其来源和状态。
将 GEML 与 LLM 一起使用
目标只有一件事:让你的模型一次编辑一个块,并进行验证——绝不要为了修改一个段落而重新读取并重新输出整个文件。要做到这一点只需一步,而具体是哪一步取决于你使用什么。
使用 Claude Code——运行这个
npx -y @geml/geml skill install
它会安装创作技能、geml CLI 和 MCP 服务器,用户全局,
对每个项目生效。无需编辑 settings.json,无需钩子;升级后重新运行即可。
(更喜欢插件?claude plugin marketplace add geml-spec/geml,然后
/plugin install geml@geml —— 同样的技能,MCP 服务器已捆绑。)
使用 DeepSeek Harness —— 添加此捆绑包
同样的设置,打包为 dsh 捆绑包 —— geml MCP 服务器加上创作和代码图技能:
dsh plugin --profile web add @geml/dsh-plugin # web = the profile dsh boots by default; use your own profile name if you run another
已列于 dshmarket 和 awesome-dsh-plugin;源码在 integrations/dsh-plugin/。
使用 Codex —— 安装插件
同样的内容再次呈现,为 Codex 打包:两个技能、MCP 服务器,以及
一个 SessionStart 钩子。在此仓库的检出中启动 Codex,它就会出现在
/plugins 中(市场源已提交在
.agents/plugins/marketplace.json);若不想克隆就添加,git-subdir
条目在 integrations/codex-plugin/。
然后在会话中说一次,项目就切换好了:
本项目使用 GEML 作为其基础文档格式;按需从中生成其他格式。
使用其他任何工具 —— 粘贴此内容,然后检查输出
没有技能可读的模型需要一次规则。粘贴下面的提示词,并
把 geml check 作为它写回内容的关卡 —— CLI 是
npm i -g @geml/geml(Node 22+)。
将文档写为 GEML:每个块都是 === type [attributes] … ===
(1 分钟了解格式列出了各类型)。有四条规则是
模型容易出错的:闭合围栏是恰好与开头等长的 = 串,
而包含 === 的正文需要更长的外层围栏;标题仅用
ATX #,没有 --- 前置元数据(元数据是 === meta);每个 #id
都唯一,且每个引用([[#id]]、text、[^id]、data=#id)
都必须能解析;没有原始 HTML。规范性规范是
GEML-spec.md。
它会用它做什么
geml list doc.geml # CALL FIRST: every block, its address, kind, lines
geml find "words" doc.geml # search block content -> an address, not a line number
geml get doc.geml '#hello' # read ONE block (a heading id = its whole section)
geml get doc.geml '#hello' --intro # a section cuts three ways: --head | --intro | --body
geml set doc.geml '#license' --in template.geml#mit # replace that block, forking another
geml add doc.geml --after '#intro' --in snippet.geml # insert a fragment (keeps its own ids)
geml revert doc.geml '#plan' --rev -1 # roll ONE block back
geml check doc.geml # 仅验证:诊断信息 + 退出码
任何节都按三种方式切分,get 和 set 皆然:--head 是标题行,
--intro 是它在第一个子标题之前的内容,--body 是其下的所有内容
——因此 --body 总是包含 --intro,当没有子标题时二者相等。
可以在不把子节拉入上下文的情况下编辑节的起始部分。
每次变更在写入前都会重新解析,如果会破坏文档则被拒绝——这正是让无人值守编辑安全的原因。其余动词
(delete、rename、history、--to md|html|geml 转换、按类型或内容哈希寻址块)见
解析器 README。
MCP 服务器
该包附带一个标准的 Model Context Protocol 服务器,因此你的 agent
一次编辑一个块,而不是重写整个文件——Markdown 和
GEML 皆然。它在 Windows、macOS 和 Linux 上本地运行;--root 是
服务器被限制的目录(使用 . 或 ${workspaceFolder} 绑定到
当前项目)。
Claude Code —— 一条命令完成设置(安装 skill、CLI 和 MCP 服务器):
sh
npx -y @geml/geml skill install
(或通过 CLI 手动注册:claude mcp add --scope user geml -- npx -y @geml/geml mcp --root .)
Cursor —— 将 .cursor/mcp.json 添加到你的项目:
json
{
"mcpServers": {
"geml": {
"command": "npx",
"args": ["-y", "@geml/geml", "mcp", "--root", "${workspaceFolder}"]
}
}
}
(或在 Cursor 设置 → Features → MCP 中:名称 geml,命令 npx -y @geml/geml mcp --root .)
Claude Desktop —— 添加到 claude_desktop_config.json:
json
{
"mcpServers": {
"geml": {
"command": "npx",
"args": [
"-y",
"@geml/geml",
"mcp",
"--root",
"/absolute/path/to/your/docs"
]
}
}
}
然后只需提出你想要的更改——“修复 FY26 表中的 Q3 行”——
agent 就会处理那一个块。你无需了解工具名称:每个工具都对应一个
CLI 动词(geml set → geml_set),因此一套词汇同时覆盖终端和
agent。
两项保证使这比让模型重写文件更好:写入在到达磁盘之前会被
解析,如果会破坏文档则连同其诊断信息一起被拒绝;并且每次写入都会先记录一个 .gemlhistory 修订——因此
一次糟糕的编辑既被阻止又可撤销(geml_revert 恢复一个块,文件其余部分逐字节不变)。路径始终限制在 --root 内,客户端
无法扩大。
将 --root 指向一个具有代码图(geml codemap build)的仓库,同一个服务器还能回答“谁调用了这个”——四个只读的 geml_codemap_ 工具,
一个客户端入口而非两个。所有工具和选项:
docs/mcp-guide.md。
生态系统与成熟度
GEML 是一个小巧、年轻的规范——但也是一个稳定的规范:1.0 已发布,可用于真实文档(本仓库自身的规范就是其中之一),配有严格的符合性测试套件、一个能通过该套件的参考实现(其版本独立于规范),以及一个开放的提案流程。
规范只有一份,并且是双语的。.gemlhistory 附属文件由 geml-history/v1 profile 定义——它是规范之上的应用层,而非规范的一部分,这也是为什么它采用 MIT 许可而规范采用 CC-BY(LICENSE-spec.md 说明了原因):
| 文档 | English | 中文 |
|----------|---------|------|
| 规范 | GEML-spec.md | GEML-spec_CN.md |
| geml-history/v1 profile | geml-history-profile.md | geml-history-profile_CN.md |
本项目发布的每一个 profile:spec/profiles/。
版本与兼容性
- 自托管——GEML-spec.geml 是用 GEML 编写的规范,要求在每次测试运行时都能干净地解析。
- 符合性测试套件 是让不同实现保持兼容的依据。
- 解析器的参考实现。 目前有 1,700+ 个单元测试,外加符合性语料库、往返序列化和端到端 CLI 运行,覆盖率由 CI 门控在 ≥95% 的行 / 语句 / 函数 / 分支。
- 前向兼容性内建于语法之中。 处理器必须对无法识别的构造优雅降级(规范 §8.2),这就是为什么添加一种块类型或一种图表格式不属于破坏性变更。类型注册表是开放的:未注册的类型名应包含连字符(acme-invoice),不含连字符的名称留给规范的未来版本(§8.5)。
- 声明符合性。 一个实现只要逐案复现符合性测试套件,就可以自称符合 GEML 1.0(§8.5)。无需许可,也无需本仓库的批准。
- 传输层面。 扩展名 .geml(版本附属文件 .gemlhistory),媒体类型 text/geml,或在需要已注册类型时使用 text/vnd.geml——text/geml 尚未在 IANA 注册。
- .geml URL 上的片段标识符指向带有该 id 的块(§0.6)——这与 #tag 在 HTML 页面上的含义不同。
我们如何思考设计
设计遵循什么
它是供人阅读的纯文本。 无需渲染器即可完全阅读——这就是为什么没有原始 HTML 逃生通道,也是为什么样式永远不能改变文档所表达的内容。
一个原语,若干模型。 每一种内容都是同一种带类型的块;扩展该格式意味着注册一个类型,而不是发明语法。类型说明它变成什么:meta 是贯穿整个文档共享的键值对,code 是位于某个位置的代码区域,data 是一个数据值,table 是一张等待被处理的网格,diagram 是托管的外部 DSL,embed 是通向某个内容源的视图
引用是一扇窗,而不是一次导航。 HTML 链接会导航:目标并不在你手中这份文档里,所以人们还是照样把它复制进来。这里要设计掉的不是死链接,而是复制的动机。代价:渲染可能需要读取多个文件,并且在无法读取时必须优雅降级。*
偏好做减法。 当某条规则滋生边界情况时,砍掉该特性,而不是去规定这些边界情况:没有下划线强调,没有 setext 标题,没有缩进代码块,没有原始 HTML。歧义在源头就被删除,而不是在测试用例中被逐一列举。代价:有些你在 Markdown 里能写的东西,在这里写不了。
没有破窗。 Markdown 的信条是永不失败——总要渲染出点什么。GEML 则相反:在构建时验证,而不是在渲染时容忍。一个悬空的 #id 是一个错误,会以非零退出码结束。稳定的 id、geml check 以及诊断目录,全都源自这一个决定。代价:一份“看起来没问题”的文档可能会让你的构建失败。
附属文件随文档同行,却不进入文档。 .geml 文件是内容的来源,并刻意保持小巧。其他任何东西都不会被塞进它里面,而是反过来指向它——比如 .gemlhistory 中的版本历史——删掉它,文档依然完全有效。代价:一条约定,无论显式还是隐式,以及两个一起同行的文件。
命令行是为智能体而构建的。 用最少的动词覆盖一切,保持彼此正交,输入输出可管道传递,选项在各命令间保持一致。
因此它拒绝什么
| 拒绝 | 原因 |
|---|---|
| 自有的图表语言 | 外部 DSL 是托管的(Mermaid、Graphviz、D2……);该格式只定义托管协议 |
| 原始 HTML 逃生舱 | 语义保持可移植,不绑定任何后端或渲染器 |
| Setext 标题 / --- frontmatter | 只用 ATX #,这样就不会与主题分隔线冲突 |
| 完整的电子表格引擎 | 逐行公式和汇总聚合就够了;没有单元格寻址、查找或宏 |
路线图
- [x] GEML 1.0 规范,含英文版和中文版,以及一致性测试套件——外加定义 .gemlhistory 附属文件的 geml-history/v1 配置档
- [x] 参考实现 @geml/geml:解析器、CLI、块级 .gemlhistory 跟踪
- [x] 面向 Claude Code、Cursor、Codex 及其他 MCP 宿主的官方 MCP 服务器(geml mcp)
- [x] codemap——整个代码库的调用图,以 GEML 编写
- [x] 发布在 Visual Studio Marketplace 上的 VS Code 扩展(发布者 geml)
- [x] 生态集成:VS Code 高亮与引用检查、tree-sitter、Obsidian、Logseq(针对实时 DB 图的双向同步)、浏览器查看器、GitHub Action、LangChain / LlamaIndex,以及 agent-harness 插件——Claude Code、Codex、Grok、DeepSeek Harness,外加 Gemini CLI 和 Kimi Code 的根清单
- [ ] 在 Logseq 市场中列出的 Logseq 插件(PR #893),以及在 xai-org/plugin-marketplace 中列出的 Grok 插件
- [ ] 其他语言的解析器(Rust / Python)——规范和一致性测试套件都是公开的,因此欢迎社区实现;我们很乐意帮忙把它们对齐
参与其中
GEML 已是 1.0,但“稳定”意味着已有的规则不会在你脚下变动,
而不是说设计已经定型。到目前为止只有一个实现,
规范背后也只有一套观点。你的想法仍然可以改变规范本身。
如果你想参与进来:
来争论这些问题:
- 文档格式应该做表格运算吗?
- GEML 到底应不应该有样式层?
- 什么时候才真正值得拥有一个 .gemlhistory sidecar?
- geml get 不带选择器时会列出块。这应该改成 geml list 吗?
- --view 会读取嵌入内容。它应该是一个标志,还是独立的动词?
或者认领一块:
| 缺口 | 现状 | 需要做什么 |
|---|---|---|
| 为更多 agent 工具安装技能 | Gemini CLI、Qwen Code 和 AGENTS.md 已经通过检测完成安装;MCP 服务器可与任何客户端配合使用 | 以同样的方式补齐其余部分:Cursor、GitHub Copilot、Cline——它们的规则文件约定变化很快,所以写之前先查当前文档 |
| 该入门指南在其他模型上的效果如何 | 只在 Claude 上验证过 | 让 GPT / Gemini / 本地模型各自根据入门指南写一批 GEML,统计有多少第一次就能通过 geml check,并报告它们总是弄错的规则——这些正是入门指南应该点名的规则 |
| 更深入的 Obsidian 集成 | 可以渲染,但还没进社区商店 | 在 CodeMirror 层进行编辑和无缝双向渲染,再加上商店提交本身。需要熟悉 Obsidian API 的人。 |
| 查看器在其他浏览器上的支持 | Chrome 可用 | Firefox / Safari 移植。 |
| 打包 RAG 集成 | LangChain / LlamaIndex 是参考实现 | 发布到 PyPI;并接入其他框架(Haystack、DSPy……)。 |
- 编写规范的第二个实现 —— 用你喜欢的任何语言编写一个新的 GEML 解析器(如何编写解析器)
- 找出规范中存在歧义的地方本身就是贡献,无论那个解析器最终是否发布。
或者提出一些新东西:
- 一个 GEP:提案、规范修改和一致性用例一起落地(流程)
或者把它用起来:
| 场景 | 位置 | 状态 |
|---|---|---|
| 从命令行使用 —— 验证、转换、按块编辑、版本历史,全部在一条命令中完成 | @geml/geml(源码 geml-parser/) | 可用 |
| 在浏览器中阅读 —— 打开任何原始 .geml 链接,它就会就地渲染:计算表格、图表、Mermaid、数学公式,并将诊断信息显示为横幅 | Chrome 应用商店 · 源码 | 可用 |
| 让智能体按块编辑 —— 一个 MCP 服务器;智能体只修改一个块,而不是重写整个文件,并且每次写入在到达磁盘之前都会经过验证 | docs/mcp-guide.md | 可用 |
| 从 DeepSeek Harness 使用 —— geml MCP 服务器加上创作和代码图谱技能,一个可安装的捆绑包 | @geml/dsh-plugin · dshmarket · 源码 | 可用 |
| 从 Codex 使用 —— 同样的内容再次打包:两个技能、MCP 服务器,以及一个 SessionStart 钩子,可从 /plugins 安装 | integrations/codex-plugin/ | 可从本仓库获取;尚未进入公共插件目录 |
| 从 Grok 使用 —— 同样的内容再一次打包:两个技能和 MCP 服务器 | integrations/grok-plugin/ | 可从本仓库获取;xai-org/plugin-marketplace 的 PR 尚未提交 |
| 将 Logseq 图谱同步为纯文本 —— 将 Logseq 2.0 DB 图谱持续同步为 GEML 文件,可寻址且对 git 友好,并以 restore 作为返回方式 | @geml/logseq-sync · 源码 | 监视器已在 npm 上;插件从发布 zip 安装 —— 市场列表(PR #893)尚未合并 |
| 将代码库转换为文档 —— 将整个调用图作为 GEML 文档树,可浏览 | geml codemap build(设计) | 可用 |
| 在你的编辑器中编写 —— 语法高亮 + 构建时引用检查 | Visual Studio 市场 · 源码 | 可用 |
| 在 Obsidian 中渲染它 — 参考解析器 + 查看器的渲染器,与 Web 端相同的代码路径 | integrations/obsidian/ | 已构建,尚未进入社区商店 |
| 接入 RAG / 智能体框架 — 块级加载器(每个块一个分块,携带 block_id)+ 智能体编辑工具 | integrations/langchain+llamaindex/ | 参考实现 |
| 无需安装任何东西即可试用 — 左侧编辑,右侧实时渲染 | Playground | 可用 |
首先需要阅读的三个文件:GOVERNANCE.md 说明决策如何
制定,CONTRIBUTING.md 说明如何提交工作,以及
CODE_OF_CONDUCT.md 说明关于人的唯一规则——
你可以尽情尖锐地反对设计,但不要针对个人。
仓库布局
spec/ 规范文档,以 .md 形式提供(英文 / 中文)以及 CC-BY 规范
许可证,包含 profiles/(应用层 — geml-history、
geml-codemap、geml-style、geml-form)和 proposals/(GEP),
两者均为 MIT
spec/in_geml_format/ 自举(dogfood):用 GEML 编写的规范,及其
.gemlhistory 附属文件
geml-parser/ 参考解析器、渲染器、CLI + codemap 工具包(TypeScript、Node 22)
integrations/ GEML 接入的各个地方:geml-viewer(浏览器扩展)、
geml-check-action(CI)、vscode、obsidian、logseq(双向
仓库同步 + 监视器)、tree-sitter(简要说明)、
langchain+llamaindex(RAG 加载器)、windows-icon
(资源管理器文件图标),以及智能体工具链插件 —
claude-plugin、codex-plugin、grok-plugin、dsh-plugin
.agents/, .claude-plugin/ 插件市场清单,使这些插件可以从
检出目录中显示出来(Codex /plugins、Claude Code /plugin)
playground/ 浏览器内 Playground(+ 本仓库的实时 geml-code-graph)
docs/ 指南、设计说明、comparisons/(COMPARISON + vs-CommonMark +
vs-XML-and-JSON)、assets(徽标,供下方 Pages 站点使用),
以及一个用于渲染的示例 .geml
.claude/skills/ Claude 技能:GEML 编写,以及代码图
.github/ CI + geml-check 工作流、MCP 注册表发布,以及 issue
模板(bug、GEP、新实现)
site/ geml-spec.github.io/geml Pages 站点:项目主页
(index.md)加上一个 Jekyll 博客(blog/,文章位于 _posts/)—
长篇的“为什么需要一种新格式”文章(英文 / 中文)就
作为其第一篇文章放在那里。cd site && bundle exec jekyll
serve 可在本地构建它;其中的 pages 作业
.github/workflows/ci.yml 在推送到 main 时构建并部署它,
将 playground/ 作为静态输出嫁接进来——其中
playground.js 在那里构建,而不是提交到仓库。
许可证与治理
代码采用 MIT 许可证(LICENSE):本仓库中的所有内容——
geml-parser/、整个 integrations/、playground/、.claude/skills/、
spec/proposals/ 中的 GEP——均适用,但规范文档除外。
规范文档采用 CC-BY-4.0 许可证(LICENSE-spec.md,
其中逐一列出了这些文档):spec/GEML-spec 和 spec/in_geml_format/。规范只有
一份;spec/profiles/ 下的配置文件属于应用层,采用 MIT 许可证。
规范不是软件,因此任何人都可以无需许可地构建符合规范的
实现——并且一旦通过一致性测试套件,
就可以称其为 符合 GEML 1.0。
关于名称的使用。 你无需获得许可即可实现 GEML、以该格式命名一个
实现(geml-rs、pygeml,或你所用语言注册表上的 geml 包),
或声明你的工具能够读写 GEML。有两个请求,二者都不是法律限制:
只有在通过一致性测试套件之后,才能称某个实现 符合
GEML 1.0;并且不要暗示本项目编写、认可或维护了该实现。规范文本
本身的署名要求,正是 CC-BY-4.0 已经提出的要求。扫码进群