15 KiB
ADR-0260:Generation-bound、Content-free 的 Plugin Package Prompt 执行
- 状态:Proposed
- 日期:2026-08-02
- 关联:D-144、D-157、D-161、D-207、D-211、D-212、D-243
背景
Plugin Package 已经能够把 Prompt 文件物化为 generation-bound 语义资源,并通过 automation publication 发布 exact active definition。但“定义已发布”不等于“可以安全 调用模型”。如果执行端临时读取 current head、直接渲染后调用 provider,会出现以下问题:
- Package 在渲染、admission 和 provider I/O 之间发生升级、disable、quarantine 或 publisher revocation,导致一次请求混合两代事实;
- provider 已产生费用,但 Run、StepRun、quota 或 audit 尚未提交;
- COMMIT 已成功但响应丢失时,重试再次调用 provider;
- 为了方便回放,把 template、参数或模型正文复制到数据库、日志、备份和 HA 副本;
- 把 Prompt 伪装成 Task 或单步 Workflow,引入错误的执行 authority,或让路由设备承担 frontier、Attempt、timer、watcher 等不必要常驻成本;
- 为协议、SQLite adapter、PostgreSQL adapter 分别新增 workspace package,继续放大 importer、构建、SBOM 和发布边界。
QingLong 同时面向小型路由设备和多节点集群,因此该能力必须满足两端同一语义:低配设备 按请求付费且禁用时零加载;集群在并发、响应丢失和数据库主从切换下仍可围栏和对账。
决策
1. Prompt 是显式 model execution
Plugin Package Prompt 不是 Task、Workflow、Trigger 或 scheduler。每次调用创建:
- 一个父
Run,从 admission 起为running; - 一个
StepRun.kind=model,从ready经唯一 ModelInvocation 链进入终态; - 一个 content-free admission receipt;
- 一个由 ModelInvocation Completion 或 Resolution 支撑的 finalization receipt。
不创建 RunAttempt、child Run、Workflow frontier、后台 cadence 或 per-Prompt worker。
2. 执行计划不可变且不含内容
执行前从 exact PluginPackageAutomationPublication 生成最多 32 KiB 的 immutable plan。
plan 必须绑定:
- Project、Package、installation、lock;
- generation、generation digest、materialized revision digest;
- publication digest、Prompt ID、Prompt definition digest;
- request、invocation、Run、StepRun、trace、发起 Subject 与 Project Policy fence;
- provider、model、max output tokens、temperature、deadline;
- parameter digest、model request digest 和 input bytes。
plan、receipt、RunEvent 和数据库索引列不得包含 template、参数值、渲染后的 message 或模型 输出正文。
3. 内容只做一次瞬态 exact rendering
Prompt template 与参数只在调用栈内存中渲染:
- required 参数缺失失败关闭;
- optional 参数缺失表示省略,不等于显式空字符串;
- 参数值不会再次递归解释其中的 placeholder;
- 不接受未声明参数;
- 单参数值与单 message 均受 64 KiB 上限;
- 生成后的输入字节、token 和 deadline 继续受 Model Gateway 硬预算约束。
渲染结果仅交给本次 Gateway 调用,admission repository 永远看不到正文。
4. Admission 必须先于 provider I/O
首次 admission 在单个存储事务中完成:
- 先检查相同 request ID 的 durable receipt;
- 对新请求复验 exact active publication;
- 在同一事务内复验发起 Subject 的 current Project/RoleBinding fence;
- 复验 current install/lock、lifecycle、quarantine、publisher start guard;
- 复验 exact materialized revision 中恰好存在一个同 digest Prompt;
- 原子写入 Run、admission event、model StepRun、StepRun mutation 和 admission receipt。
任何 guard、identity、digest、外键或计数冲突都整体回滚。exact replay 必须先于 current guard,使已提交 winner 在 Package 后续 disable、withdraw 或 upgrade 后仍能收敛。
5. ModelInvocation 是唯一 provider fence
Prompt executor 不直接持久化 provider 状态,也不建立第二套 invocation 状态机。所有 provider I/O 必须经过 D-157 的 ModelInvocation:
ready→running、Run/Event/Mutation 和 ModelInvocationStart 原子提交;- quota reservation、price quote、usage 和 completion 沿用既有 Gateway policy;
running→succeeded|failed|timed_out|lost由唯一 StepRun mutation chain 决定;- durable start 已存在但 completion 缺失时禁止 provider replay;
- completion 为
outcome_unknown且没有人工 Resolution 时保持不可判定; - Resolution 为 retry 时父 Prompt Run 保持运行,cancel/fail 才能终态化。
6. 父 Run 从终态证据收敛
finalization repository 在一个事务中读取 exact admission、Run/StepRun 与 ModelInvocation Completion/Resolution:
- Completion
succeeded|failed|timed_out直接决定父 Run 状态; outcome_unknown只接受绑定同一 completion digest 的终态 Resolution;- Run version 与 event sequence 必须保持相等并 CAS 增加一次;
- final RunEvent 只保存 evidence digest、final StepRun digest、plan digest 和低敏状态;
- finalization receipt 与 Run/Event/StepRun/evidence 必须可双向复验。
7. Replay 不承诺返回模型正文
首次 live 调用可以把 GenerateResult 返回当前 caller。exact replay 返回 admission 与
finalization receipts,但 result 为 null,绝不再次调用 provider。
如果产品需要跨请求读取输出,必须后续引入显式、受保留期与加密策略约束的 Artifact sink, 并只在 ModelInvocation completion 中保存 bounded output ref/hash/bytes。不得把正文补进 Prompt admission/finalization 表。
8. 双方言与权限边界
SQLite:
- AI feature 未 active 时不加载 Prompt executor、不建新进程或连接;
- 与 Local ModelInvocation 共享同一 operation authority;
- admission/finalization 使用短
BEGIN IMMEDIATE; - 两张 Prompt 表属于可选
ModelInvocation*schema allowlist,不污染主 schema readiness; - 停用 AI 时既有 exact replay 可读,但新 mutation 受 feature fence。
PostgreSQL:
- 使用独立
ql3_aimigration stream 的 9007/9008; model_invocation_prompt_admissions和model_invocation_prompt_finalizations为 append-only;ql3_runtime只有SELECT, INSERT,没有UPDATE, DELETE;- AI schema 提供 Prompt 专属
SECURITY DEFINERplugin_package_prompt_admission_snapshot,不借用名称错误的 Workflow 入口; - snapshot 复用核心
plugin_package_automation_start_allowed,并锁定 publication 与 materialized revision;它同时按 Subject type/ID、Project version 与最新 RoleBinding version 复核 API admission 的 immutable policy fence; - mutation 使用带 5 秒 statement、2 秒 lock timeout 的短 SERIALIZABLE transaction, 对 serialization/deadlock 最多重试三次。
9. Package 与部署边界
实现全部留在既有 @qinglong/ai package 的显式 subpath:
plugin-package-prompt-execution;plugin-package-prompt-executor;local-plugin-package-prompt-admission-storage;postgres-plugin-package-prompt-admission-storage;postgres-plugin-package-prompt-application。
这些是同一可选 AI capability 的协议和 adapter,不满足独立部署、进程、权限域或重依赖 隔离价值,因此不得新增 workspace package。
Edge/Standalone 通过 Local application 在 AI active 后动态装配。Cluster 通过显式、默认
关闭的 PostgreSQL application composition 装配;disabled 分支不得打开 Pool、执行 readiness
或加载 provider,active 分支必须按“只读 migration/ACL readiness → bounded recovery → provider
credential load”顺序启动。该 composition 不增加进程、listener、端口或 workspace package。
Cluster Control 只在显式注入 promptExecution capability 时增加
POST /api/v3/projects/{projectId}/packages/{packageName}/prompts/{promptId}/executions;
默认 allowlist 仍只有 Run read/cancel。该 route 复用现有 bearer authentication、认证前
rate shield、Project Policy、durable security audit、TLS/body/response/in-flight/request
timeout,并使用独立 model.invoke permission。客户端只能提交 publication digest,不能
提交 publication JSON;服务端从受限 snapshot 解析 exact publication,随后 admission
事务再次复核 publication 与 policy fence。route 不新增进程、listener、端口、依赖或包。
已实现证据
- immutable Prompt plan、非递归 rendering、digest/size/identity normalizer;
- SQLite admission/finalization repository 与 Local AI lazy composition;
- PostgreSQL 9007/9008、Prompt 专属 snapshot 与 SERIALIZABLE repository;
- Prompt executor 的 first execution、safe resume、completion repair 与 exact replay;
- SQLite 真库覆盖原子 admission、publication withdrawal 后 replay、target drift rollback、 parent Run finalization、provider exactly-once 与内容排除;
- 原生 Linux arm64 Node 24.18.0 的
router-stress-ci(128 MiB、0 swap、0.5 CPU)与edge-release-ci(256 MiB、0 swap、1 CPU)已直接运行 Local AI active product vertical: 正式 install/materialize/publication → Prompt execute →succeeded@v5→ exact replay, provider 恰好一次、零 RunAttempt、durable SQLite 不含私有输入/输出。两档 Prompt process peak RSS 分别为92282880/90951680bytes,数据库 logical/allocated growth 均为0, cgroupmemory.peak分别为128229376/129253376bytes,零 max/OOM; - 同一两档门已纳入 ModelInvocation start/completion 的 7 点/profile、14 场景
SIGKILL → reopen → exact replay矩阵;Prompt admission/finalization 外层事务再覆盖 10 点/profile、20 场景,其中 16 个 COMMIT 前 crash 全回滚、4 个 COMMIT 后 crash durable, exact replay/content-free/integrity/foreign key 全绿并报告promptAdmissionFinalizationCrashProven=true。所有 CI 证据仍固定physicalPowerLossProven=false,不把进程崩溃或 tmpfs 文件增长冒充闪存写放大或真实断电; - crash fixture 通过
@qinglong/ai的 test-only workspace devDependency 使用正式@qinglong/local-sqlitebaseline migration 与 Package repositories;AI production dependencies 仍只有@qinglong/runtime-core,没有新增 importer、生产依赖或部署闭包; - AI suite 共 115 项:112 pass、3 个无 URL 条件 skip、0 fail;新增覆盖 Prompt 外层事务 20 点 crash matrix、Cluster disabled 零加载、exact migration/ACL readiness、先 recovery 后 provider、幂等资源释放与 readiness 失败不开 provider;
- runtime-core 430/430 通过,
model.invoke仅 owner/admin/operator 可直接使用,viewer 拒绝,agent 保持require_approval;Cluster Control 167 项为 165 pass、2 个外部服务 条件 skip、0 fail,覆盖 strict body、subject/fence 传递、request abort、低敏错误、live result 与 content-free replay receipt,以及未注入时 route 不存在; - 独立 PostgreSQL 18 真库使用 migration、package-executor、runtime 三个非特权账号,完成
Cluster application readiness、Package install/materialize/publish、Prompt execute、
ModelInvocation completion、父 Run finalization 与 replay;provider 调用一次,Run 收敛到
succeeded@v5,durable JSON 不含私有参数和模型输出; - PostgreSQL 18.4 arm64 physical-streaming HA 门在最新 9008 checksum 上
gates.passed=true:9001—9008 history/ACL、runtime 只读 migration history、Prompt admission/finalization、provider exactly-once、content-free durable facts 在晋升前复制并在 timeline 1→2 后完全一致;旧主先 fencing,再经pg_rewind以只读同步 standby 重加入, 新 product service 从数据库解析 publication,RoleBinding 撤销后旧 policy fence 被拒绝且 provider 调用数保持一次;gates.passed=true,临时容器/网络/卷零残留,用户现有 evidence control-plane 未被触碰; - 通用三节点 K3s/CloudNativePG 1.30/PostgreSQL 18.4 HA 门继续全绿,覆盖 TLS 1.3、 identity/certificate rotation、failover/outage recovery、CNI/RBAC 与 durable facts。
接受门与非目标
本 ADR 保持 Proposed,直到以下门完成:
- 在 PostgreSQL physical streaming timeline promotion、旧主 fencing、rewind/rejoin 后,独立证明 9007/9008 migration、ACL、admission/finalization receipt 完全一致;
- Cluster application 显式装配 Prompt executor,且不存在 repository 可用即等于 route 上线的隐式行为;
- 产品 API transport 完成受认证 Principal、Project Policy、
model.invoke、rate/body/ timeout budget、request abort 和低敏错误映射;agent 自动化仍必须先通过 approval 产品链; - API 入口明确区分 live result 与 content-free receipt replay;UI/MCP 可复用该 API, 但不作为本 ADR 接受前提;
- durable output 已由 ADR-0261 单独冻结 envelope、transaction、authorization、retention 与 GC 边界;双方言原子 adapter、产品读取和 maintenance authority 完成前保持不可达;
- 在固定 128/256 MiB 原生 Linux CI envelope 中补充 active application、RSS、SQLite logical/allocated growth、exact replay、内容排除、ModelInvocation 与 Prompt admission/ finalization 外层事务 SIGKILL/reopen 矩阵;disabled 零数据库/provider 加载继续由组合测试 证明;
- 在固定物理低配路由设备上采集最终 application artifact 的 active RSS、真实数据盘/
闪存写放大与受控断电重启矩阵。CI 报告必须保持
physicalPowerLossProven=false,完成前不得形成最低支持声明。
以下不属于本 ADR:
- Prompt 定时执行或 Workflow 内嵌 Prompt;
- Agent、多模型编排、Tool calling 或向量检索;
- Secret 自动插值;
- provider 自动重试;
- Prompt/输出正文默认采集;
- 新 workspace package、daemon、watcher、listener 或 per-Prompt timer。
被拒绝方案
- 把 Prompt 包装成单步 Workflow:引入不需要的 frontier、Attempt、cancellation 与 recovery ownership,放大路由设备成本。
- 把 Prompt 当普通 Task:绕过 Model Gateway policy、quota、price、usage 与唯一 model StepRun fence。
- provider I/O 后补 Run:数据库故障时产生无 durable owner 的外部费用和结果。
- exact replay 自动再调 provider:COMMIT response loss 或 caller retry 会重复计费。
- 持久化 template、参数和输出:扩大 Secret、个人数据、日志、备份和 HA 副本泄漏面。
- 复用 Workflow snapshot 函数:实现可行但 authority 名称错误,会把 Prompt 生命周期 隐式耦合到 Workflow admission;因此改为 AI schema 内 Prompt 专属入口。
- 为每个 adapter 拆 package:没有独立部署或依赖价值,违反 D-207 并增加供应链成本。