你大概见过这种场面:给 Agent 一个任务,它开始自由发挥——调用一堆不相关的工具、把 8000 token 的工具表塞满上下文、跑到一半忘了目标、最后给你一个"大概是这样"的答案。你调了三个小时的 prompt,第二天换个说法又不行了。
根本原因不是模型不够聪明,是你把"该怎么做"也交给了模型去现场决定。
人类团队里没人这么干活。一份作业怎么做、一份合同怎么审、一次发布怎么走——这些是流程,是白纸黑字定下来的,不是每次临场发挥。只有流程定不下来的时候,我们才用"你看着办"。
LLM-R 把这个常识搬回 Agent:流程由人写死,模型只负责执行它被分配的那一小块。
| 通用 Agent | LLM-R | |
|---|---|---|
| 流程 | 模型临场推理 | 人写死的一张图 |
| 工具 | 全部塞进上下文 | 每个容器只见自己那几个 |
| 上下文 | 什么都往里塞 | 容器之间互不可见 |
| 能力 | 运行时才发现 | 设计期就是一张静态表,可审 |
| 出问题 | 不知道它为什么这么干 | 每一步都有结构化轨迹 |
| 界面 | 一个通用的臃肿 AI | 绑定工作种类 |
「避免干扰」在这里对两个对象成立:模型(推理质量)和用户(操作效率)。
LLM-R 里只有一个概念:SWF(Stable WorkFlow)——一张人工写死的工作流声明。它是纯数据。
每个 AMZ 是一个独立容器:它有自己的系统提示词、自己那几个工具、自己的模型。容器之间默认互不可见。边上的 when 是由确定性求值器算的——不是模型说了算。
AMZ = 一个 DSH 对话。 LLM-R 不造运行时、不造权限引擎、不造记忆库——DSH 已经把这些做好了,LLM-R 只负责按声明把它们编排起来。
两套主题,跟随系统或显式选一个;SWF 自带的界面跟着一起切。
暗色是底色(一个 runtime 控制台该长这样),亮色是为白天和投影准备的。
两边共用同一套令牌——强调色的各种透明度用 color-mix 从 --accent 派生,
不各写一遍,否则每加一档透明度都要在两边各写一次,迟早漏。
SWF 自带的页面(
ui/*.html)也长在这套令牌上,所以它们不用改一行就跟着换主题。 主题值由服务器注入到页面的首帧(?__theme=)——iframe 是 sandbox 的、读不到 localStorage,不能指望它自己去查。
LLM-R 的界面只有两种人用,所以只有两种样子。
普通用户面对的是自己的 AGT:填「你要干什么」→ 开始 → 该确认的地方停下来问你 → 成品直接显示在页面上。
作者面对的是声明本身:校验结果、调用图、AMZ 有效值表、能力表面、审查凭据,改完可以直接试跑,不必先建一个 AGT。
调用图是从声明真的画出来的,不是示意图:
- 层号 = 从入口出发的最长路径;分支左右并排,环会被校验器提前拒掉
- 灰线是
when边,虚线是else兜底边,虚框是终态 - 跑完之后,走过的节点和边会依次点亮(那是轨迹回放,数据来自真实执行,不是动画特效)
- 「在此等你确认」的节点是琥珀色——你一眼能看出流程卡在哪
暂停与恢复是两次调用,界面会把两段轨迹拼起来显示。 只显示后一段的话,图上前几步永远是黑的——那几步是真跑过的。
零运行时依赖(只需要 Node,ajv 装上就多一层结构校验,不装也能跑)。
git clone https://github.com/LLYlab/LLM-R.git && cd LLM-R
node tools/llmr/homework.smoke.cjs # 19 项:把一份真作业声明从头跑到尾,不花一分钱
node tools/llmr/selftest.cjs # 39 项:加载器
node tools/llmr/server.cjs # → http://127.0.0.1:8735/WebUI 起来了就能点着走完整个流程——默认 echo 后端,不产生任何模型花费:
第一次运行:judge_task → extract_info → confirm_homework ← 停在这,等人确认
恢复运行: read_pdf → translate → solve_cn → solve_en → draw_figures
→ render_docx → verify_docx → show_result ← 成品
要接真模型:设置 → 模型 API 填 Base URL 和 Key。注意这里配的只是接口与凭据——
具体调哪个模型由 SWF 决定,每张 SWF 的每个 AMZ 各自声明 model。
(Key 明文存在本地 settings.json,永不回传浏览器;共享机器上建议用环境变量。)
特写:把一份老师发的作业,做成排版好看、公式可编辑的 Word,并显示在页面上。
不是 demo 玩具,是真的跑通的 7 步 / 11 个容器(swfs/homework.swf.json):
| 效果 | 声明里的节点 |
|---|---|
| 1 判断是作业任务 | judge_task |
| 2 从中提取信息 | extract_info |
| 3 与用户确认作业 | confirm_homework → 暂停 |
| 4 自动读取作业 pdf | read_pdf |
| 5 开始转换 | translate → solve_cn → solve_en → draw_figures → render_docx |
| 6 搞好 | verify_docx(公式没成 OMML 就先绕 fix_formulas 修一次) |
| 7 显示作业于页面上 | show_result |
每次运行都必然经过第 3 步的暂停——这是设计,不是意外。
「暂停」用已经存在的 output.signal 通道表达(声明 ask 字段),没有为它动 schema。
两条不走主线的出口同样是硬编码的:不是作业 → not_homework;是作业但抽不出信息 → 也是 not_homework(不硬凑)。
① 工具注册表(tools/toolbox.json)—— 40 个工具、6 个类别、两种表面(AMZ / 界面)。
校验器的"工具是否存在"和"属于哪一类"都从这里读:
可复用实现全局注册,SWF 只声明选用。 工具表不是每个流程自己编的。
② 本地接入 —— 图形化选文件 / 选目录 / 另存为 / 剪贴板 / 打开路径 / 资源管理器定位。
SWF 自带的界面跑在 sandbox="allow-scripts" 的 iframe 里,碰不到你的硬盘——
这是对的。所以这类事由宿主代劳:页面调 llmr.pick.file(),宿主弹真的系统对话框。
参数走环境变量传进 PowerShell,不拼字符串。
③ 成本画像 —— node tools/llmr/run.cjs <swf> --report:
成本画像
模型调用 2 次(另有 1 次暂停,不花模型)
走到了 3/15 个容器,跳过 12 个:not_homework, read_pdf, …
确定性判定 1 次 · 依赖模型的判定 2 次
工具面:LLM-R 实际背 1 条 · 整表口径 4×2=8 条 → 省 7 条
"省 token"得是能打印的数字,不能是口号。
| SWF | 一句话 |
|---|---|
coding-auto-run |
你只说清要什么,不看中间源码——改坏了自己回头修 |
coding-help |
先写 Goal 锚住,然后像 Copilot 边写边问——它不改你的文件 |
coding-draft |
在壳里写 AI: 指令,它保留你的壳、只填肉 |
story-novel |
大纲 / 新章 / 编辑渲染 / 亲自编辑 |
story-dnd |
AI 当 DM,回合循环走环控区 |
skill-to-swf |
把 skill 文档转成 SWF 草稿——签名要你自己签 |
skill-to-swf 撞上了一条自己的原则:「SWF 永远是人的产物」。
解法不是装看不见,而是让机制去拦:它交出的草稿绝不写 _review.surfaceHash,
所以在 --mode=import 下一定过不了。流程可以由 AI 起草,签名不行。
「稳」的含义要说清:不是"模型不会说谎",是模型说谎时流程也只在设计好的格子里走。
tools/llmr/examples.selftest.cjs拿五种模型性格各跑一遍来证明这件事。
|
398 项断言,全绿 外加两个 fuzz 套件(3.6 万份畸形输入,0 崩溃)与一次一致性审计—— 23 个错误码 + 19 项检查的项数,实现 ↔ 两份规格三方对齐。 |
22 处缺陷,几乎全部由「执行」抓出 从 其中 4 处是纯静态分析根本不可能发现的——它们只在"真的点下去"的那一刻才现形。 最近一轮最有意思的一处:给审计加了「项数必须一致」这条之后,它第一次运行就抓出 11 处漂移—— 文档里同时写着 15 项、16 项、17 项。没人改错,只是没有任何东西在管这个数字。 |
这个项目有一条自己的原则,写在规格里:
规格的缺陷只能靠执行暴露。 所以先把校验器和执行器做出来,比继续写文档划算得多。
而且这条原则适用于文档本身:既然人会漂,就给文档装一条会失败的检查。
auditcodes.cjs 就是这么来的——它现在是这个仓库里唯一"读文档"的东西,而它是个程序。
LLM-R 不是"装上就什么都能干"的通用 AI。 它面向中型/大型、任务性质明确、流程可复用的工作。
作者门槛很高。 不会写 SWF 的用户只剩三条路:
- 拷大佬的——SWF 可以导入导出、可以分享
- 提 issue 求作者——但作者是瓶颈
- 用兜底的临时工作流
LLM-R 是给"有能力定义自己工作流的人"的工具。
这是明确的设计取舍,不是待修的缺陷。想让 AI 替你决定一切的人,不该用 LLM-R——市场上那样的产品已经很多了。
| 我想… | 看 |
|---|---|
| 搞懂 LLM-R 是什么、为什么这么设计 | LLMR-设计规格.md ← 唯一权威规格 |
| 看结构约束(机器可读) | llmr.schema.json ← v1.0,已冻结 |
| 看声明语法与 17 项校验细则 | LLMR-声明格式规格-02.md |
| 看 19 项检查的算法与错误码目录 | LLMR-校验器规格.md |
| 抄一个能干活的真货 | swfs/homework.swf.json |
| 看实测证据、缺陷清单、当前状态 | docs/开发记录.md |
| 看被取代的旧设计(过程记录) | docs/history/ |
一个项目愿意不做什么,比它宣称能做什么更能说明它是谁。
LLM-R 只做「把 SWF 跑起来」这一件事,其余全用现成的。
编辑界面、权限引擎、记忆库、嵌套、训练平台、性能框架、专家路由—— 不在 LLM-R 的范围里,LLM-R 也不逐项权衡它们。 在那些方向上,LLM-R 借用宿主已有的东西:会话、工具与插件、模型路由、界面槽位、大块内容存储。
而「不做嵌套」这一条有个副作用,值得单独写出来,因为它是个陷阱:
嵌套会击穿 LLM-R 的审查机制——子 SWF 里的工具绑不进父的能力表面,而审查哈希只覆盖父。 改子 SWF 不改哈希 → 审查被绕过。
LLM-R 的答案是不做嵌套。任何要做嵌套的系统,都必须让 能力表面递归展开、哈希覆盖整棵子树——否则"可审查"这个承诺在嵌套出现的第一天就失效了。
这条不是理论推演:它是 LLM-R 在设计能力表面时被自己的机制逼出来的结论。
- 固定优先——SWF 固定、AMZ 固定、工具/权限静态绑定、UI 固定。唯一的"流动"是判定之前。
- 隔离优先——容器之间互不可见;隔离由"独立会话"结构性提供,不需额外机制。
- 硬编码优先于自主——能用确定性规则就别用模型;能用固定边就别用 tool use。
- 避免干扰,对模型与用户同样成立。
- 可审查性——能力是设计期的一张静态表,所以陌生 SWF 能被审。前提是这张表完整。
- 所有可复用实现全局注册,SWF/AMZ 只声明选用。
- 能力盒子里得真装东西——没有专用工具的 AMZ 只有省钱,没有能力增益。
- 规格的缺陷只能靠执行暴露。
- 边界纪律——❌ 项是承诺,不是"暂时没做"。每加一项都合理,加起来就不是轻量了。
LLM-R turns agent workflows into data you can read, review, and version — instead of letting the model improvise.
Every other agent framework is adding autonomy. LLM-R removes it: a human writes a fixed workflow (an SWF), the model only executes the narrow slice it is assigned inside an isolated container (AMZ). No dynamic tool selection, no tool table in context, no cross-container leakage.
Zero runtime dependencies. 240 passing assertions. Schema frozen at v1.0. The whole runtime — validator, loader, executor, WebUI — is ~3,900 lines of plain JavaScript.
Start here: LLMR-设计规格.md — the single authoritative spec (Chinese).





