mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
96 lines
8.0 KiB
Markdown
96 lines
8.0 KiB
Markdown
# ADR-0289:按执行 Request ID 恢复并读取 Durable Package Prompt 输出
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-08-08
|
||
- 关联:D-85、D-87、D-156、D-157、D-213、D-244、D-257、ADR-0261、ADR-0263、ADR-0267、ADR-0274、ADR-0275、ADR-0276、ADR-0288
|
||
|
||
## 上下文
|
||
|
||
ADR-0288 允许调用方在 Prompt execute 响应丢失后,用自己持久化的 `executionRequestId` 精确恢复 Run 状态;但其
|
||
结果刻意排除了 Artifact identity 和正文。现有 Prompt output read 又要求调用方同时保存服务端返回的 `runId`、
|
||
`artifactId` 和 `artifactDigest`。当首次 durable execute 已成功提交而 HTTP/CLI 响应丢失时,这三个事实同样无法取得,
|
||
形成“知道执行成功、却不能取回已付费输出”的产品断点。
|
||
|
||
Cluster 已定义 caller-selected Artifact read route,但显式 AI production composition 尚未装配其 read authorizer 和
|
||
capability。Local application runtime 支持 durable output,Owner CLI 却只接受 `live_only`。仅新增一个 Artifact reference
|
||
发现接口会把重试、权限、retention、密钥解析和解密继续留给调用者,不能构成 QingLong 3.0 的恢复闭环。
|
||
|
||
## 决策
|
||
|
||
1. 新增共享结果 schema `qinglong/plugin-package-prompt-execution-output-read-result@v1`。输入固定为强认证
|
||
principal 与 `projectId/packageName/promptId/executionRequestId`;成功结果返回既有 immutable Artifact reference
|
||
和受界 `GenerateResult`,缺失统一返回 `not_found`。
|
||
2. SQLite/PostgreSQL 在现有 Prompt admission `request_id` 主键上精确定位,只连接 succeeded finalization、
|
||
ModelInvocation completion、Run、StepRun 和 immutable output Artifact,并要求 `step.output_ref=artifact_id`。
|
||
查询固定 `LIMIT 2`,只投影 `runId/artifactId/artifactDigest`,不得读取 `artifact_json`、ciphertext 或 key material。
|
||
3. 定位成功后必须委托既有 `PluginPackagePromptOutputReadService`,保持 metadata→`artifact.read` Policy→retention/
|
||
tombstone→key resolve→AES-GCM decrypt→key wipe 的顺序。新服务不得直接解密、绕过 tombstone 或请求 active key。
|
||
4. Local `ql3-prompt` command-file schema v1 增加 `prompt.execution.output.read`,权限和 allowed audit operation 均固定为
|
||
`artifact.read`/`prompt.execution.output.read`。读取只装载 SQLite、output Artifact/retention repository 和显式
|
||
`promptOutputKeyringPath`,不得加载 Provider authority、Provider Secret 或 Model Gateway。
|
||
5. Local `prompt.execute` 接受显式 `durable_artifact + retentionPolicy`。只有该模式要求并装载
|
||
`promptOutputKeyringPath` 与 completion coordinator;`live_only` 继续禁止配置 output keyring,确保默认路由设备路径
|
||
零 Artifact/key/repository 增量。exact replay 返回相同 Artifact reference,不再次调用 Provider。
|
||
6. Cluster 显式 AI Profile 新增
|
||
`GET /api/v3/projects/{projectId}/packages/{packageName}/prompts/{promptId}/executions/{executionRequestId}/output`,
|
||
operation 为 `prompt.execution.output.read`,permission 为 `artifact.read`。生产组合仅在 Prompt output keyring opt-in
|
||
时同时装配现有 caller-selected Artifact route 和新 request-keyed route;默认 AI-free control 不注册二者。
|
||
7. absent execution、live-only、未完成、失败、cross-target、policy deny、已 tombstone 或 GC 后 Artifact 均统一映射
|
||
`not_found`/HTTP 404,不暴露目标存在性。内部 shape、digest 或 identity 漂移映射 503,正文不得进入错误和审计。
|
||
8. 本增量不新增 workspace package、生产 dependency、migration、表、索引、Pool、端口、listener、timer、watcher、
|
||
cache、队列、controller 或部署单元。三个新 AI 文件全部进入既有 `prompt-output/` domain,workspace 保持 19 包且
|
||
root-file hard cap 不变。
|
||
|
||
## 被拒绝的方案
|
||
|
||
- **把 Artifact reference 加入 ADR-0288 的 content-free inspection**:会把 `run.read` 与 `artifact.read` 内容边界混合,
|
||
也让所有状态轮询暴露存储与 retention 元数据。
|
||
- **只返回 reference,不返回正文**:调用者仍需拼接第二次 API、保存 digest 并处理 tombstone,响应丢失恢复仍不是一个
|
||
原子产品动作。
|
||
- **用 request ID 计算 Artifact ID 后直接解密**:无法证明 Package/Prompt、terminal completion、StepRun output_ref 和
|
||
Artifact digest 的完整绑定。
|
||
- **把正文复制到 admission/finalization receipt**:扩大热表、备份、审计和 HA 泄漏面,破坏 immutable 加密 Artifact
|
||
authority。
|
||
- **为 recovery 建 projection 表或新索引**:现有 request 主键和 terminal join 已能精确定位,新增状态会产生双写和
|
||
Edge migration 成本。
|
||
- **新增 workspace package 或常驻 recovery worker**:没有独立部署或故障域收益,并增加小设备 importer、内存和运维
|
||
表面积。
|
||
|
||
## 当前证据
|
||
|
||
- 共享 service 5/5、Cluster route 3/3;SQLite/PostgreSQL locator 都使用 request 主键和 terminal joins,定位 SQL
|
||
不读取 Artifact envelope/ciphertext,并覆盖 malicious widened reference/result、cross-target 与 missing。
|
||
- Local Owner CLI 定向新用例通过:显式 durable execute、exact replay、按原 execution request 读取正文、重复读取和
|
||
allowed audit 均成功;Provider generate 仅一次,读取前后 Provider load 计数不变。
|
||
- Cluster AI/production/route 定向 19/19;output opt-in 同时装配 caller-selected 和 request-keyed read,默认 AI-free
|
||
composition 保持不装配。
|
||
- AI、Local Owner CLI、Cluster Control 与 Local Application 的完整 package suites 均零失败;完整 19-package clean
|
||
build/test 与 backend 1,110(1,108 pass/2 skip)全绿。cluster dependency、package boundary、Edge import、Cluster
|
||
deployment、CloudNativePG 与 local image 六项审计 compatible;十档 artifact/RSS 全绿,Edge 3,614,826 bytes,
|
||
最大 Standalone Application AI 6,102,447 bytes,默认非 AI closure 未新增 keyring/Artifact importer。
|
||
- package ledger 保持 19 包、`singleSourcePackages=[]`;762 个 source 中 49 个仅为受审公开/binary/composition 根入口,
|
||
713 个实现位于领域目录。新增 AI/Cluster 实现进入既有 capability,未以恢复能力为由新增微包或根层平铺。
|
||
- PostgreSQL 18.4 arm64 physical-streaming HA 已实跑通过。轮换后的新 key durable output 在 primary 通过原
|
||
execution request 精确解密,standby WAL replay 与 promoted runtime-role decrypt 结果一致,cross-target 保持
|
||
`not_found`,报告不含正文;`pluginPackagePromptExecutionOutputRecoveryIsExactAndContentFree` 与
|
||
`pluginPackagePromptExecutionOutputRecoveryReplicatesAndSurvivesPromotion` 均为 true。timeline 1→2、旧主
|
||
fencing/rewind/read-only synchronous rejoin、两套 fresh control replica、`gates.passed=true` 与最终
|
||
`ql3-ha-*` container/network/volume 零残留全绿。
|
||
|
||
## 接受条件
|
||
|
||
1. AI、Local Owner CLI、Cluster Control 定向与完整测试全绿;双方言 exact/cross-target/live-only/tombstone/replay/
|
||
malformed-result 测试覆盖。
|
||
2. 完整 19-package clean build/test、backend、Edge/Cluster dependency、package boundary、deployment/local image 与十档
|
||
artifact/RSS 门无回归;默认 Edge/Standalone 非 AI 与 live-only 闭包不得新增 keyring/Artifact importer。
|
||
3. PostgreSQL physical-streaming HA 必须证明 durable Artifact、request-keyed locator 和 decrypt read 在 primary、standby
|
||
WAL replay、promotion 后 runtime role 上保持一致;cross-target/content-free audit、timeline、rewind、fresh replicas 和
|
||
零残留继续全绿。
|
||
4. 刷新 GitNexus 后对所有已修改符号重新检查影响,并运行 `detect_changes` all/compare `develop`。
|
||
|
||
## 后续边界
|
||
|
||
- Prompt execution history、输出列表、全文搜索、流式增量输出与跨 Package 查询仍需独立索引、分页和可见性 RFC。
|
||
- UI 可在 execute 超时后先读 content-free execution 状态;只有用户显式查看 durable output 时调用本内容接口,避免
|
||
状态轮询反复解密和传输正文。
|