RFC: Render System — Resolution 衰减模型 + LiquidJS 模板引擎 #36

Closed
opened 2026-05-31 03:17:09 +00:00 by xiaoju · 2 comments
Owner

背景

json-cas 节点需要人类可读的渲染能力。不同场景需要不同的展开深度——概览只看顶层,调试需要完整展开。之前讨论过离散的 verboseLevel 0-5 方案,现改为连续的 resolution 衰减模型,更灵活、语义由模板定义而非引擎规定。

核心设计

1. Resolution 衰减模型

  • resolution: (0, 1] 的浮点数,表示当前节点的"分辨率"
  • 衰减传播: 子节点 resolution = 父节点 resolution × decay
  • epsilon 剪枝: resolution ≤ epsilon 时渲染为 cas:<hash> 引用,停止递归
  • resolution = 0: 特殊值,显式强制渲染为 cas:<hash>

示例(resolution=0.8, decay=0.5, epsilon=0.01):

root: 0.8
  ├─ child: 0.4
  │   └─ grandchild: 0.2
  │       └─ great-grandchild: 0.1
  │           └─ ...: 0.05
  │               └─ ...: 0.025
  │                   └─ ...: 0.0125
  │                       └─ leaf: 0.00625 ≤ epsilon → cas:3MXQR7VKP1
  └─ ...

2. CLI 参数

参数 默认值 含义
--resolution 1.0 根节点分辨率
--decay 0.5 未指定衰减的子节点默认系数
--epsilon 0.01 低于此值渲染为 cas:<hash>
# 深度展开
ucas render <hash> --resolution 1 --decay 0.8

# 浅层概览
ucas render <hash> --resolution 0.5 --decay 0.3

3. Decay 优先级

模板显式指定 > CLI --decay > 引擎默认 0.5

4. 无模板默认行为

没有模板时,使用 YAML 渲染 + 二元截断:

  • resolution > epsilon → 展开为内联 YAML
  • resolution ≤ epsiloncas:<hash>

统一使用 CLI --decay 或引擎默认值作为所有子节点的衰减系数。

5. 模板引擎:LiquidJS

选择 LiquidJS 作为全局模板引擎(替代 mustache),理由:

  • 内置条件比较({% if resolution > 0.5 %}),无需注册 helper
  • {% render %} 是 Liquid 保留 tag,语义天然匹配递归渲染
  • 沙箱特性,用户自定义模板更安全
  • TypeScript 原生,活跃维护
  • Shopify / Jekyll / GitHub Pages 大量使用,LLM 训练数据覆盖充足

模板示例:

{% if resolution > 0.5 %}
## {{ payload.name }}
{% for role in payload.roles %}
  {% render role, decay: 0.7 %}
{% endfor %}
{% elsif resolution > epsilon %}
**{{ payload.name }}** ({{ payload.roles | size }} roles)
{% else %}
`cas:{{ hash }}`
{% endif %}

6. 差异化衰减

同一层不同子节点可设不同衰减系数:

{% render payload.roles decay: 0.9 %}
{% render payload.metadata decay: 0.3 %}

重要字段慢衰减(看得更深),次要字段快衰减(早截断)。

后续工作

  • json-cas render 命令实现
  • LiquidJS 集成 + 自定义 render tag
  • 模板注册/发现机制(按 node schema 匹配模板)
  • workflow 侧 mustache → Liquid 迁移

参考


小橘 🍊(NEKO Team)

## 背景 json-cas 节点需要人类可读的渲染能力。不同场景需要不同的展开深度——概览只看顶层,调试需要完整展开。之前讨论过离散的 `verboseLevel` 0-5 方案,现改为连续的 resolution 衰减模型,更灵活、语义由模板定义而非引擎规定。 ## 核心设计 ### 1. Resolution 衰减模型 - **resolution**: `(0, 1]` 的浮点数,表示当前节点的"分辨率" - **衰减传播**: 子节点 `resolution = 父节点 resolution × decay` - **epsilon 剪枝**: `resolution ≤ epsilon` 时渲染为 `cas:<hash>` 引用,停止递归 - **resolution = 0**: 特殊值,显式强制渲染为 `cas:<hash>` 示例(resolution=0.8, decay=0.5, epsilon=0.01): ``` root: 0.8 ├─ child: 0.4 │ └─ grandchild: 0.2 │ └─ great-grandchild: 0.1 │ └─ ...: 0.05 │ └─ ...: 0.025 │ └─ ...: 0.0125 │ └─ leaf: 0.00625 ≤ epsilon → cas:3MXQR7VKP1 └─ ... ``` ### 2. CLI 参数 | 参数 | 默认值 | 含义 | |------|--------|------| | `--resolution` | `1.0` | 根节点分辨率 | | `--decay` | `0.5` | 未指定衰减的子节点默认系数 | | `--epsilon` | `0.01` | 低于此值渲染为 `cas:<hash>` | ```bash # 深度展开 ucas render <hash> --resolution 1 --decay 0.8 # 浅层概览 ucas render <hash> --resolution 0.5 --decay 0.3 ``` ### 3. Decay 优先级 ``` 模板显式指定 > CLI --decay > 引擎默认 0.5 ``` ### 4. 无模板默认行为 没有模板时,使用 YAML 渲染 + 二元截断: - `resolution > epsilon` → 展开为内联 YAML - `resolution ≤ epsilon` → `cas:<hash>` 统一使用 CLI `--decay` 或引擎默认值作为所有子节点的衰减系数。 ### 5. 模板引擎:LiquidJS 选择 LiquidJS 作为全局模板引擎(替代 mustache),理由: - 内置条件比较(`{% if resolution > 0.5 %}`),无需注册 helper - `{% render %}` 是 Liquid 保留 tag,语义天然匹配递归渲染 - 沙箱特性,用户自定义模板更安全 - TypeScript 原生,活跃维护 - Shopify / Jekyll / GitHub Pages 大量使用,LLM 训练数据覆盖充足 模板示例: ```liquid {% if resolution > 0.5 %} ## {{ payload.name }} {% for role in payload.roles %} {% render role, decay: 0.7 %} {% endfor %} {% elsif resolution > epsilon %} **{{ payload.name }}** ({{ payload.roles | size }} roles) {% else %} `cas:{{ hash }}` {% endif %} ``` ### 6. 差异化衰减 同一层不同子节点可设不同衰减系数: ```liquid {% render payload.roles decay: 0.9 %} {% render payload.metadata decay: 0.3 %} ``` 重要字段慢衰减(看得更深),次要字段快衰减(早截断)。 ## 后续工作 - [ ] json-cas render 命令实现 - [ ] LiquidJS 集成 + 自定义 render tag - [ ] 模板注册/发现机制(按 node schema 匹配模板) - [ ] workflow 侧 mustache → Liquid 迁移 ## 参考 - [LiquidJS 文档](https://liquidjs.com/) - [Shopify Liquid 参考](https://shopify.github.io/liquid/) --- 小橘 🍊(NEKO Team)
Author
Owner

补充:模板注册机制 + 内置 Schema + CLI 子命令

1. @ucas 保留变量域

模板通过 Variable System 注册,使用保留命名空间:

@ucas/template/text/<schema-hash>
  • @ucas — json-cas 系统保留域,用户变量不可使用此前缀
  • template/text — 类别(Liquid 文本模板)
  • <schema-hash> — 目标 node type 的 schema hash

需要调整变量名校验规则:允许首段以 @ 开头(当前只允许 [a-zA-Z0-9._-])。

2. 内置 Schema 别名

init / bootstrap 时自动注册常用 schema,CLI 支持 @ 前缀快捷引用:

别名 JSON Schema
@schema 元 schema(bootstrap 自身)
@string { "type": "string" }
@number { "type": "number" }
@object { "type": "object" }
@array { "type": "array" }
@bool { "type": "boolean" }

用法:

# 等价操作
ucas put @string template.liquid
ucas put 5KQNR3VWP1M2X template.liquid

3. template CLI 子命令

封装底层 put + var set 为一步操作:

# 注册模板(自动存 CAS + 绑定变量)
ucas template set <schema-hash> <template-file>
ucas template set <schema-hash> --inline '{% if resolution > 0.5 %}...'

# 查看已注册模板内容
ucas template get <schema-hash>

# 列出所有已注册模板
ucas template list

# 删除模板
ucas template delete <schema-hash>

template set 内部流程:

  1. 确保 @string schema 存在
  2. store.put(stringSchemaHash, templateContent) — 存模板到 CAS
  3. var.set("@ucas/template/text/<schema-hash>", contentHash) — 绑定变量

4. render 模板发现

ucas render <hash> 时自动按 node 的 type hash 查找模板:

node.type → 查找 var "@ucas/template/text/<node.type>"
  → 找到 → LiquidJS 渲染
  → 没找到 → 默认 YAML 渲染(二元截断)

小橘 🍊(NEKO Team)

## 补充:模板注册机制 + 内置 Schema + CLI 子命令 ### 1. `@ucas` 保留变量域 模板通过 Variable System 注册,使用保留命名空间: ``` @ucas/template/text/<schema-hash> ``` - `@ucas` — json-cas 系统保留域,用户变量不可使用此前缀 - `template/text` — 类别(Liquid 文本模板) - `<schema-hash>` — 目标 node type 的 schema hash 需要调整变量名校验规则:允许首段以 `@` 开头(当前只允许 `[a-zA-Z0-9._-]`)。 ### 2. 内置 Schema 别名 在 `init` / `bootstrap` 时自动注册常用 schema,CLI 支持 `@` 前缀快捷引用: | 别名 | JSON Schema | |------|-------------| | `@schema` | 元 schema(bootstrap 自身) | | `@string` | `{ "type": "string" }` | | `@number` | `{ "type": "number" }` | | `@object` | `{ "type": "object" }` | | `@array` | `{ "type": "array" }` | | `@bool` | `{ "type": "boolean" }` | 用法: ```bash # 等价操作 ucas put @string template.liquid ucas put 5KQNR3VWP1M2X template.liquid ``` ### 3. `template` CLI 子命令 封装底层 `put` + `var set` 为一步操作: ```bash # 注册模板(自动存 CAS + 绑定变量) ucas template set <schema-hash> <template-file> ucas template set <schema-hash> --inline '{% if resolution > 0.5 %}...' # 查看已注册模板内容 ucas template get <schema-hash> # 列出所有已注册模板 ucas template list # 删除模板 ucas template delete <schema-hash> ``` `template set` 内部流程: 1. 确保 `@string` schema 存在 2. `store.put(stringSchemaHash, templateContent)` — 存模板到 CAS 3. `var.set("@ucas/template/text/<schema-hash>", contentHash)` — 绑定变量 ### 4. `render` 模板发现 `ucas render <hash>` 时自动按 node 的 type hash 查找模板: ``` node.type → 查找 var "@ucas/template/text/<node.type>" → 找到 → LiquidJS 渲染 → 没找到 → 默认 YAML 渲染(二元截断) ``` --- 小橘 🍊(NEKO Team)
Author
Owner

RFC 全部交付完成

Phases:

  • Phase 1: 内置 schema 别名 — PR #42 merged
  • Phase 2: template CLI — PR #44 merged
  • Phase 3: render 核心引擎 + resolution 衰减 — PR #43 merged
  • Phase 4: LiquidJS 集成 — PR #45 merged
  • 补充: renderDirect + --pipe — PR #49 merged
  • Cleanup: #46 merged
  • E2E bugfix: #57 #59 #60 #61 merged, #63 pending

— 小橘 🍊(NEKO Team)

RFC 全部交付完成 ✅ **Phases:** - Phase 1: 内置 schema 别名 — PR #42 merged - Phase 2: template CLI — PR #44 merged - Phase 3: render 核心引擎 + resolution 衰减 — PR #43 merged - Phase 4: LiquidJS 集成 — PR #45 merged - 补充: renderDirect + --pipe — PR #49 merged - Cleanup: #46 merged - E2E bugfix: #57 #59 #60 #61 merged, #63 pending — 小橘 🍊(NEKO Team)
This repo is archived. You cannot comment on issues.
No Label
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/json-cas#36