开发者指南 / 设计动机

为什么需要面向 Agent 的语言?

Octoscript 面向一种不同的作者:模型会提出程序、读取诊断、修正输出,再请求宿主执行。目标是让「意图 → 程序 → 操作」有一份小巧、可检查、可执行的约定。安全性来自语言、运行时与宿主边界的共同设计。

为什么不直接生成 Python 或 JavaScript?

需要成熟软件生态,或者让开发者在隔离环境中运行通用代码时,Python 和 JavaScript 都是很好的选择。Octoscript 关注另一种场景:应用需要在自己的运行时内部接受生成的工作流,只开放少量明确的服务,而不把整个通用开发环境交给模型。

设计问题直接采用通用语言运行时Octoscript 的选择
模型可以生成什么?语言和生态很大,应用还需定义允许使用的子集。发布明确的规范语法,提供有界预检和结构化诊断。
权限从哪里来?取决于嵌入方式、沙箱、导入机制及暴露的 API。工具由宿主注册,授权由宿主签发,源码不能给自己授权。
如何融入 UI 事件循环?应用自行提供运行时集成与调度。Rust 嵌入与宿主驱动的延迟调用支持让出和恢复执行。
如何限制工作量?运行时与隔离策略需要补齐相应限制。明确限制源码、token、嵌套、指令、截止时间、字符串、栈、调用帧和受跟踪的堆。
Agent 如何修错?诊断跨语言、包与应用 API 分散。版本化 profile、无副作用检查、格式化、工作流审查与有界编辑工具。

这不是说 Python/JavaScript 无法安全使用,而是主动选择不同的约束:用更小、更容易描述和执行的语言表面,换取宿主对生成程序的控制。已有的 Python 或 JavaScript 服务仍然可以封装成宿主工具。

先设计生成闭环,再设计语法

Agent 先获得语言 profile 和宿主当前的工具目录,再生成源码或纯数据的工作流草案。检查器在不加载模块、不执行副作用的前提下返回结构化诊断。Agent 根据诊断修正提案,宿主再决定是否创建计划并签发能力。

语言 profile + 工具目录 + 用户意图
  → 源码/工作流草案
  → 无副作用检查与审查
  → 根据诊断修正
  → 宿主批准 + 有界授权
  → 执行/让出/恢复
  → 结果与可审计状态

语法通过不等于允许执行。 工具目录描述能力,不授予权限;工作流草案表达计划,不自动成为有执行权的计划。这些分离让模型能够编程,同时让应用继续掌握操作边界。

一次调用,经过哪些边界?

以应用提供的 text.echo 为例。模型只生成下面这段程序;text.echo 是演示工具,实际服务由应用注册。

use mod.tool
let task = "prepare release notes"
let result = tool.call("text.echo", task)
result
阶段输入与产物这一阶段真正证明什么?
1. 描述语言octoscript profile 返回版本、规范入口、检查与运行预算、无副作用工具和授权模型。说明可生成的语言,不创建运行时或授权。
2. 描述工具宿主读取 CapabilityRuntime::tool_catalog(),向模型提供当前注册工具。名称、参数和限制是接口说明;目录不保证调用者有执行权。
3. 检查源码octoscript check 检查规范 v0.2 语法,再检查与底层解析器的兼容性。语法成立。不会加载模块、调用工具,也不证明业务结果正确。
4. 决定权限应用根据用户请求、当前策略与调用参数选择工具和预算,签发 capability lease。只有宿主签发的有效授权能让调用越过能力边界。
5. 预留并执行每次实际调用检查工具名、有效授权、剩余次数和输入限制;JSON 工具还可执行 schema 校验。授权通过后才进入 Rust 适配器,返回值也要满足输出限制。
6. 返回或恢复本地调用返回;延迟调用让出控制,由宿主完成外部工作后恢复。应用事件循环保持控制,脚本不能自行创造外部操作句柄或恢复凭证。

例如,目录列出了 text.echo,但当前 lease 只允许 calendar.read,上面的程序仍然被拒绝。允许调用一次也不意味着允许第二次;修改工具名、超出输入字节数,或尝试使用另一会话的延迟结果,都不能继承这次授权。

工具目录里,哪些字段真的重要?

目录来自当前宿主注册表,按名称稳定排序。模型应使用这份目录,不猜测系统里“应该存在”什么工具。

字段 / 概念含义应用应如何使用
name、description固定工具名称及用途;名称是有界的小写 ASCII 标识符。将用途解释给模型,按准确名称查找注册能力。
envelope、dispatch文本或 JSON 输入输出;本地或外部调度方式。选择 call / call_json 或延迟调用接口。
调用、尝试、字节预算限制调用次数、外部操作尝试次数、输入输出大小。max_attempts 由宿主使用,不给脚本增加重试 API。
input / output schemaJSON 的结构与类型约束。仅当 contract_enforced: true 时,Rust 工具边界确实执行这些 schema;否则它们只是描述。文本工具始终为 false。
deferred / stream 限制延迟截止时间;分块数量、大小与脱敏后的总量。约束宿主调度与工作线程,不为脚本开放任意计时器或流。

默认目录最多注册 128 个工具,序列化总量最多 512 KiB,宿主可收紧这些边界。注册能力、展示目录、向某个工作流授予能力,是三个分别发生的动作。

草案如何成为有权执行的计划?

模型可以提交如下 JSON。source 使用上一节的语法;这里的换行通过 JSON 转义表示。

{
  "format_version": 1,
  "steps": [
    {
      "id": "prepare",
      "source": "use mod.tool\nlet note = tool.call(\"text.echo\", \"draft\")"
    }
  ]
}

草案只接受 format_version、steps 和步骤内的 id、source;未知字段会被拒绝。不能在 JSON 中塞入 approved: true、lease、检查点、工具结果或外部操作 ID 来获得权限。入口最多接收 2 MiB JSON、1,024 个步骤和 1 MiB 解码后的源码总量。

WorkflowDraft::from_json  → 解码有界数据,拒绝额外字段
review                   → 无副作用语法检查 + 直接工具调用提示
engine.plan_draft        → 创建引擎拥有的计划,只记录 Planned
宿主选择逐步策略          → 决定允许的工具、参数边界和预算
engine.approve…          → 把计划与宿主批准绑定
engine.execute           → 每次调用仍检查实际权限

review 的调用提示适合帮助审阅,但不能解析别名、控制流或动态工具名,也可能被截断。宿主不能只从提示里提取工具名就自动授权。审批与运行时预留检查要共同约束真实调用。

多个步骤需要共享数据时,宿主另外提供只含 JSON 的 workflow.input 和 workflow.outputs.<step_id>。上下文最多 64 KiB、64 层;不允许函数、原生句柄、循环引用和非有限数字。输出不满足约束时,下一步不会开始。数据本身不会携带执行权。

出错、等待与重试分别由谁负责?

情况行为
语法错误检查器返回位置与诊断,模型修正源代码后重新提交。
普通脚本错误v0.2 的 try … catch … 可选择替代值;不会得到可操作的底层错误对象。
资源预算耗尽运行中止。硬性资源错误不能被捕获后继续消耗预算。
外部调用等待脚本通过延迟句柄等待,宿主驱动完成、截止时间和恢复。
外部副作用已经发生捕获异常不会撤销付款或消息。幂等键、对账、重试和持久恢复由宿主适配器与工作流引擎负责。

这些界限使失败能够被解释和处置;它们不承诺任意外部服务具备事务或 exactly-once 语义。

小巧、安全、可靠、高效,分别意味着什么?

小巧: 生成和检查围绕一份规范的工作流语言展开。继承的解析器能够识别某种 UI 或 shader 语法,并不意味着工作流也允许使用它。UI 有单独且明确的入口。

安全: 默认没有工具权限。经过审查的 Rust 适配器暴露操作、受限输入输出与宿主选择的授权。独立运行时屏蔽继承来的 UI/调试及无界原生入口。资源耗尽会中止执行,硬性资源限制不是普通应用异常,不能被脚本捕获后继续消耗资源。

可靠: 把源码检查与副作用分开,把数据与执行权分开,把重试与重复执行的授权分开。持久状态、幂等与恢复策略由宿主负责。捕获一个异常,不意味着付款等外部操作已经回滚。

高效: 复用可嵌入的 Rust VM 与宿主服务,限制保留的数据,让延迟调用返回应用事件循环。目标是较小的集成负担和可约束的执行成本。这里不把设计目标包装成未经测量的 Python/JavaScript 性能、体积或 token 成本优势。

VM 上限覆盖的是它实际跟踪的存储和操作,并非整个进程的内存配额,也不能限制任意可信 Rust 适配器的内部分配。需要运行不可信原生代码时,仍需操作系统隔离。预检限制源码、token 和语法嵌套;执行限制指令、截止时间、栈、调用帧、字符串和受跟踪堆。宿主需要给自己的原生适配器另设资源与隔离策略。

为什么从 Splash 出发?

Makepad 的 Splash 已经提供 Rust 脚本引擎、对象组合、UI 集成与实时迭代能力。从这里出发,可以保留生成式应用行为和丰富 UI 表达之间的联系。

Octoscript 在这套基础上为生成工作流作专门设计:规范的生成语法、有界检查和执行、受控宿主绑定、可审查数据流、能力授权与面向 Agent 的工具。UI 路径则通过自己的宿主继续复用 Splash 组件组合能力。

Fork 维护在 OctoSense-org/makepad 的 octoscript 分支。Octoscript 通过固定版本的 makepad-script 依赖使用它,这个仓库没有再复制一份 VM。VM 加固进入共享 fork;工作流和 UI 可以共享实现,但不必共享 VM 实例、堆或宿主绑定。上游策略记录了来源与更新规则。

A2App:让提案变成可运行的应用

这里的 A2App 指 Agent 能描述行为、组合 UI、检查实际生成的组件、操作它们,并根据运行证据修正源码。模型面对的不仅是一份语言规范,还有可以查询和操控的运行中应用。

继续阅读 Splash、UI 宿主与 Agent 工具,以及两条从设计到应用的流程。

←全部文档