# 开发历程：从向上管理工具到 MoonBit Rete 引擎

> 本文是 MoonUp Rete 的开发复盘，记录三次定位调整、关键设计取舍、AI 辅助范围与人工审查边界。

## 起点：一个被驳回的垂直应用

项目最初形态是"向上管理决策引擎"：MoonBit 实现 OGSM、证据分级、防偏见护栏、假设空间、案例沉淀等 7 个模块，面向职场晋升沟通场景。9 月赛初审被驳，理由是：

> 场景较特殊，生态需求和应用场景都很窄，而且感觉更偏向落地产品，复用性不强。

这个判断当时有道理——代码里"老板""晋升""背锅"等职场语义和引擎逻辑缠在一起，评委 30 秒内看不到可复用的基础设施层。

## 第一次转型：抽通用引擎层

把 `lib/engine` 抽成与业务语义解耦的 Rule / Fact / RuleEngine，向上管理降级为预置规则包。补了客服分流、风控审核两个跨领域 Demo，证明同一引擎换规则即可复用。

这次转型方向对了，但 10 月赛初审再次被驳：

> 项目核心功能与 MoonBit 已有的规则引擎生态具有较大重叠；目前差异性不足；如果方向选定为完整支持 Rete 规则引擎，拓展价值会大大增加。

调研 `YeeHh2004/moonrule` 后确认：moonrule 是无状态单条 JSON 表达式校验（API 准入、表单校验），我们做的是线性匹配规则引擎——两者确实重叠，因为线性匹配本质就是"逐条求值"，没有解决 moonrule 没解决的问题。

## 第二次转型：实现真正的 Rete 网络

这是项目的实质性转向。核心实现：

- **alpha/beta/terminal 网络**：规则 DSL 编译成共享网络，单条件测试进 alpha，跨事实变量绑定进 beta，完整匹配落 terminal；
- **工作内存**：insert / update / retract，事实变化时沿网络增量传播，不做全量重匹配；
- **冲突消解**：salience（优先级）→ specificity（条件数）→ recency（新近度）→ rule id 字典序，确定性排序；
- **match-select-act 循环**：动作修改事实 → 增量传播 → 连锁激活，带 fire limit 硬刹车；
- **OR / NOT 复合条件**：`||` 顶层 OR 组（每组独立编译网络、共享 terminal），`!` 否定（扫描工作内存全集，须配正条件）；
- **WASM 导出**：浏览器本地运行，零外部依赖。

## 关键设计取舍（为什么这么做，而不是那样做）

### 为什么 retract 用全表过滤而不是指针级联删除

经典 Rete retract 需要沿网络做指针级联删除：事实从 alpha 撤下后，共享该 token 的部分匹配沿 beta 网络逐层失效。我们用了更简单的实现：`remove(fid)` 直接扫描所有 alpha.mem / beta.mem / terminal.agenda，删掉含该 fid 的 token。

取舍：全表过滤的清理开销与事实数线性相关，在几十到几百条规则规模下完全够用；指针级联需要维护双向指针和 justification 图，实现复杂度显著上升。我们把规模定位写进文档，不假装是生产级实现。

### 为什么不做 TMS（Truth Maintenance System）

rust-rule-engine / Drools 都有 TMS：一个事实可以被多个理由支持（justification），撤回一个理由时自动检查是否还有其他理由支撑。我们明确不做——这要求维护理由依赖图，与"嵌入式 MVP"定位冲突。我们的 retract 语义是"事实没了就没了，未执行激活一并撤销，已执行动作不回滚"，这是更简单但可预测的语义。

### 为什么 OR 必须在顶层，不支持 `a && (b || c)`

OR 组的实现是"每组独立编译一条 alpha/beta 链、共享 terminal"。如果允许 OR 内嵌于 AND，需要把条件解析成表达式树（先拆 OR 再拆 AND），复杂度上一个台阶。MVP 阶段明确写进路线图，不假装支持。

### 为什么加 no-loop 机制

开发 AI Agent 集成示例时发现一个真问题：两条规则修改同一个事实时，即使各自动作幂等，也会互相触发导致乒乓激活（A 改 ticket → B 重新激活 → B 改 ticket → A 重新激活……）。修复方式是同一轮 `fire_all` 内一条规则 id 只触发一次——这是经典 Rete 的 no-loop 语义，简单且可预测。这个 bug 是写 demo 时暴露的，不是凭空设计的。

## AI 辅助范围与人工审查边界

AI 工具（LLM 编码助手）参与了：

- 代码脚手架与样板代码；
- 接口签名的多版讨论；
- 测试用例初稿；
- 文档起草。

以下内容由人工完成并逐条审查：

- **Rete 网络架构**：alpha/beta/terminal 划分、OR 组独立编译、neg 不建网络节点而扫全集——这些是踩坑后手动修正的；
- **冲突消解策略**：三层排序的顺序是反复权衡的结果；
- **DSL 语义边界**：OR 限顶层、neg 须配正条件、跨事实引用仅限 beta join；
- **no-loop 修复**：发现乒乓激活后，人工定位根因并选择"一轮内 rule id 唯一"的最简方案；
- **全部测试**：人工设计断言场景，AI 只辅助写初稿。

## 为什么是 MoonBit 需要这个

MoonBit 生态（mooncakes.io ~4300 包）目前只有表达式校验类规则包（moonrule），缺少"多事实工作内存 + 跨事实 join + 增量传播 + 动作执行"这一层。MoonUp 不追求复刻 Drools / rust-rule-engine 的生产级能力，而是在 MoonBit 原生（含 WASM 浏览器端）做一个边界清晰、可测试、可审计的嵌入式 Rete MVP。

设计哲学延续一句话：**LLM 负责把自然语言归约成结构化事实，引擎负责在确定性规则下裁决并给出可审计轨迹**。这是 AI 代理的可靠决策后端——LLM 可能会幻觉，但规则执行不会。

## 冲刺阶段记录（10 月，初筛通过后）

进入开发期后按验收矩阵（完成度 / 工程质量 / 可解释性 / 用户体验）补齐的增量：

- **JSON 规则加载（`parse_rules_json`）**：规则集以 JSON 字符串注入，必填字段校验 + 带定位的错误信息。至此"规则即数据"闭环：业务侧换 JSON 文件即可切换规则集，引擎代码零修改。新增 CLI 段 `[0F]` 与 WASM 演示页 JSON Config tab 实证。
- **性能基准（`tools/bench`）**：以 `@env.now()` 毫秒计时生成 N 规则 × M 事实。实测 50×100 → 16ms、200×500 → 104ms、500×1000 → 758ms、1000×2000 → 4915ms。诚实写明：编译近似线性、insert+fire 超线性（terminal 全量重匹配），定位目标规模为几十到几百条规则，未做跨实现对比。
- **mooncakes 发布 + 独立接入验证**：`mw1696803-hub/moonup@0.1.0` 发布成功。在一个全新独立 MoonBit 项目中实测 `moon add mw1696803-hub/moonup` → 写跨事实规则 → `moon run main` 输出 `fired rules: 1 / order.status=hold`——这是"可被其他项目直接依赖的第三方库"定位的最直接证据。
- **评审点名边界的回归测试**：单独负号 `-`、重复运算符 `>>`、小数 `1.2`、双负号 `--1`、空条件——逐项验证不得意外命中；正常负数仍可解析。累计 96 个测试全过。
- **文档一致性治理**：测试数与文档逐处同步、演示页 5 tab 与 README 措辞统一、修复过一次 PowerShell 写入导致的 GBK 乱码（改用字节级扫描 + 精确替换）。

边界仍是边界：单轮匹配-执行（不做多轮链式推理承诺）、规则通过 API/JSON 注入（配置热加载属于路线图）、几十到几百条规则规模（不追求 Rete 级别优化）。
