chore(i18n): refresh zh-CN translations
This commit is contained in:
parent
5190e4aa51
commit
d7573a4e2f
@ -2,18 +2,18 @@
|
||||
read_when:
|
||||
- 更新 OpenClaw
|
||||
- 更新后出现问题
|
||||
summary: 安全更新 OpenClaw(全局安装或源码),以及回滚策略
|
||||
title: 更新
|
||||
summary: 安全更新 OpenClaw(全局安装或源码安装),以及回滚策略
|
||||
title: 更新中
|
||||
x-i18n:
|
||||
generated_at: "2026-04-26T09:10:59Z"
|
||||
generated_at: "2026-04-26T23:29:43Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: e40ff4d2db5f0b75107894d2b4959f34f3077acb55045230fb104b95795d9149
|
||||
source_hash: 9ce89a768e26a5b8775794180e0f23da63040205507b0f6013210bdaafdd4856
|
||||
source_path: install/updating.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
保持 OpenClaw 为最新版本。
|
||||
让 OpenClaw 保持最新。
|
||||
|
||||
## 推荐:`openclaw update`
|
||||
|
||||
@ -32,13 +32,13 @@ openclaw update --tag main
|
||||
openclaw update --dry-run # 仅预览,不实际应用
|
||||
```
|
||||
|
||||
`--channel beta` 会优先选择 beta,但当 beta 标签缺失或版本旧于最新稳定版时,运行时会回退到 stable/latest。如果你想一次性使用原始 npm beta dist-tag 进行包更新,请使用 `--tag beta`。
|
||||
`--channel beta` 会优先选择 beta,但当 beta 标签缺失或比最新稳定版更旧时,运行时会回退到 stable/latest。若你想在一次性的包更新中使用原始 npm beta dist-tag,请使用 `--tag beta`。
|
||||
|
||||
渠道语义请参阅[开发渠道](/zh-CN/install/development-channels)。
|
||||
关于渠道语义,参见 [Development channels](/zh-CN/install/development-channels)。
|
||||
|
||||
## 在 npm 和 git 安装之间切换
|
||||
|
||||
如果你想更改安装类型,请使用渠道。更新器会保留你在 `~/.openclaw` 中的状态、配置、凭证和工作区;它只会更改 CLI 和 Gateway 网关所使用的 OpenClaw 代码安装来源。
|
||||
当你想更改安装类型时,请使用渠道。更新器会保留你在 `~/.openclaw` 中的状态、配置、凭证和工作区;它只会更改 CLI 和 Gateway 网关所使用的 OpenClaw 代码安装方式。
|
||||
|
||||
```bash
|
||||
# npm 包安装 -> 可编辑的 git 检出
|
||||
@ -48,30 +48,42 @@ openclaw update --channel dev
|
||||
openclaw update --channel stable
|
||||
```
|
||||
|
||||
先使用 `--dry-run` 运行,以预览精确的安装模式切换:
|
||||
先使用 `--dry-run` 运行,以预览确切的安装模式切换:
|
||||
|
||||
```bash
|
||||
openclaw update --channel dev --dry-run
|
||||
openclaw update --channel stable --dry-run
|
||||
```
|
||||
|
||||
`dev` 渠道会确保存在一个 git 检出,对其进行构建,并从该检出安装全局 CLI。`stable` 和 `beta` 渠道使用包安装。如果 Gateway 网关已经安装,`openclaw update` 会刷新服务元数据并重启它,除非你传入 `--no-restart`。
|
||||
`dev` 渠道会确保存在一个 git 检出,构建它,并从该检出安装全局 CLI。`stable` 和 `beta` 渠道使用包安装。如果 Gateway 网关已经安装,`openclaw update` 会刷新服务元数据并重启它,除非你传入 `--no-restart`。
|
||||
|
||||
## 替代方式:重新运行安装器
|
||||
## 备选方式:重新运行安装器
|
||||
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
```
|
||||
|
||||
添加 `--no-onboard` 可跳过新手引导。若要通过安装器强制指定安装类型,可传入 `--install-method git --no-onboard` 或 `--install-method npm --no-onboard`。
|
||||
添加 `--no-onboard` 以跳过新手引导。若要通过安装器强制指定安装类型,请传入 `--install-method git --no-onboard` 或 `--install-method npm --no-onboard`。
|
||||
|
||||
## 替代方式:手动使用 npm、pnpm 或 bun
|
||||
如果 `openclaw update` 在 npm 包安装阶段之后失败,请重新运行安装器。安装器不会调用旧的更新器;它会直接运行全局包安装,并且可以恢复部分更新的 npm 安装。
|
||||
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method npm
|
||||
```
|
||||
|
||||
若要将恢复固定到特定版本或 dist-tag,请添加 `--version`:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method npm --version <version-or-dist-tag>
|
||||
```
|
||||
|
||||
## 备选方式:手动使用 npm、pnpm 或 bun
|
||||
|
||||
```bash
|
||||
npm i -g openclaw@latest
|
||||
```
|
||||
|
||||
当 `openclaw update` 管理全局 npm 安装时,它会先运行标准的全局安装命令。如果该命令失败,OpenClaw 会使用 `--omit=optional` 重试一次。该重试有助于在无法编译原生可选依赖的主机上完成安装,同时如果回退方案也失败,仍会保留原始失败信息。
|
||||
当 `openclaw update` 管理全局 npm 安装时,它会先运行常规的全局安装命令。如果该命令失败,OpenClaw 会使用 `--omit=optional` 重试一次。这个重试有助于处理那些无法编译原生可选依赖的主机,同时在回退也失败时仍保留原始失败信息。
|
||||
|
||||
```bash
|
||||
pnpm add -g openclaw@latest
|
||||
@ -83,30 +95,30 @@ bun add -g openclaw@latest
|
||||
|
||||
### 全局 npm 安装与运行时依赖
|
||||
|
||||
OpenClaw 会将打包后的全局安装在运行时视为只读,即使全局包目录对当前用户可写也是如此。内置插件的运行时依赖会被暂存到可写的运行时目录中,而不是直接修改包树。这样可以避免 `openclaw update` 与正在运行的 Gateway 网关或本地智能体在同一次安装期间修复插件依赖时发生竞争。
|
||||
OpenClaw 会将打包的全局安装在运行时视为只读,即使当前用户对全局包目录有写权限也是如此。内置插件的运行时依赖会暂存到一个可写的运行时目录中,而不是修改包树。这样可以避免 `openclaw update` 与正在运行的 Gateway 网关或本地智能体发生竞争,因为后者可能会在同一次安装期间修复插件依赖。
|
||||
|
||||
某些 Linux npm 环境会将全局包安装到由 root 拥有的目录下,例如 `/usr/lib/node_modules/openclaw`。OpenClaw 通过同样的外部暂存路径支持这种布局。
|
||||
某些 Linux npm 配置会将全局包安装到 root 拥有的目录中,例如 `/usr/lib/node_modules/openclaw`。OpenClaw 通过相同的外部暂存路径支持这种布局。
|
||||
|
||||
对于加固的 systemd 单元,请设置一个包含在 `ReadWritePaths` 中的可写暂存目录:
|
||||
对于强化过的 systemd 单元,请设置一个可写的暂存目录,并将其包含在 `ReadWritePaths` 中:
|
||||
|
||||
```ini
|
||||
Environment=OPENCLAW_PLUGIN_STAGE_DIR=/var/lib/openclaw/plugin-runtime-deps
|
||||
ReadWritePaths=/var/lib/openclaw /home/openclaw/.openclaw /tmp
|
||||
```
|
||||
|
||||
如果未设置 `OPENCLAW_PLUGIN_STAGE_DIR`,OpenClaw 会在 systemd 提供 `$STATE_DIRECTORY` 时优先使用它,然后回退到 `~/.openclaw/plugin-runtime-deps`。修复步骤会将该暂存目录视为由 OpenClaw 管理的本地包根目录,并忽略用户的 npm prefix/全局设置,因此全局安装的 npm 配置不会将内置插件依赖重定向到 `~/node_modules` 或全局包树中。
|
||||
如果未设置 `OPENCLAW_PLUGIN_STAGE_DIR`,OpenClaw 会在 systemd 提供 `$STATE_DIRECTORY` 时使用它,否则回退到 `~/.openclaw/plugin-runtime-deps`。修复步骤会将该暂存区视为 OpenClaw 自有的本地包根目录,并忽略用户的 npm prefix/global 设置,因此全局安装的 npm 配置不会把内置插件依赖重定向到 `~/node_modules` 或全局包树中。
|
||||
|
||||
在执行包更新和内置运行时依赖修复之前,OpenClaw 会尽力对目标卷执行磁盘空间检查。空间不足会生成一条包含已检查路径的警告,但不会阻止更新,因为文件系统配额、快照和网络卷在检查后仍可能发生变化。实际的 npm 安装、复制和安装后验证仍然是最终依据。
|
||||
在包更新和内置运行时依赖修复之前,OpenClaw 会尽力对目标卷执行一次磁盘空间检查。空间不足会产生一条警告,并包含所检查的路径,但不会阻止更新,因为文件系统配额、快照和网络卷可能会在检查后发生变化。实际的 npm 安装、复制和安装后验证仍然是最终依据。
|
||||
|
||||
### 内置插件运行时依赖
|
||||
|
||||
打包安装会将内置插件运行时依赖保留在只读包树之外。在启动期间以及执行 `openclaw doctor --fix` 时,OpenClaw 仅会为以下内置插件修复运行时依赖:在配置中处于活动状态、通过旧版渠道配置处于活动状态,或由其内置清单默认启用的插件。仅存在已持久化的渠道凭证状态,并不会触发 Gateway 网关启动时的运行时依赖修复。
|
||||
打包安装会将内置插件运行时依赖保留在只读包树之外。在启动时以及执行 `openclaw doctor --fix` 期间,OpenClaw 只会为以下内置插件修复运行时依赖:在配置中处于激活状态、通过旧版渠道配置处于激活状态,或由其内置清单默认启用。仅持久化的渠道凭证状态本身不会触发 Gateway 网关启动时的运行时依赖修复。
|
||||
|
||||
显式禁用具有最高优先级。已禁用的插件或渠道不会仅因为存在于包中就触发其运行时依赖修复。外部插件和自定义加载路径仍需使用 `openclaw plugins install` 或 `openclaw plugins update`。
|
||||
显式禁用优先。如果某个插件或渠道已被禁用,它不会仅因为存在于包中就获得运行时依赖修复。外部插件和自定义加载路径仍应使用 `openclaw plugins install` 或 `openclaw plugins update`。
|
||||
|
||||
## 自动更新器
|
||||
|
||||
自动更新器默认关闭。可在 `~/.openclaw/openclaw.json` 中启用:
|
||||
自动更新器默认关闭。在 `~/.openclaw/openclaw.json` 中启用它:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -124,9 +136,9 @@ ReadWritePaths=/var/lib/openclaw /home/openclaw/.openclaw /tmp
|
||||
|
||||
| 渠道 | 行为 |
|
||||
| -------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| `stable` | 等待 `stableDelayHours` 后,在 `stableJitterHours` 范围内按确定性抖动进行应用(分散发布)。 |
|
||||
| `beta` | 每隔 `betaCheckIntervalHours` 检查一次(默认:每小时),并立即应用。 |
|
||||
| `dev` | 不会自动应用。请手动使用 `openclaw update`。 |
|
||||
| `stable` | 等待 `stableDelayHours`,然后在 `stableJitterHours` 范围内以确定性抖动方式应用(分散发布)。 |
|
||||
| `beta` | 每 `betaCheckIntervalHours` 检查一次(默认:每小时),并立即应用。 |
|
||||
| `dev` | 不自动应用。请手动使用 `openclaw update`。 |
|
||||
|
||||
Gateway 网关也会在启动时记录一条更新提示(可通过 `update.checkOnStart: false` 禁用)。
|
||||
|
||||
@ -140,7 +152,7 @@ Gateway 网关也会在启动时记录一条更新提示(可通过 `update.che
|
||||
openclaw doctor
|
||||
```
|
||||
|
||||
迁移配置、审计私信策略,并检查 Gateway 网关健康状态。详情请参阅:[Doctor](/zh-CN/gateway/doctor)
|
||||
迁移配置、审计私信策略,并检查 Gateway 网关健康状态。详情参见:[Doctor](/zh-CN/gateway/doctor)
|
||||
|
||||
### 重启 Gateway 网关
|
||||
|
||||
@ -166,9 +178,9 @@ openclaw doctor
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
提示:`npm view openclaw version` 会显示当前已发布的版本。
|
||||
提示:`npm view openclaw version` 会显示当前已发布版本。
|
||||
|
||||
### 固定提交(源码)
|
||||
### 固定到某个提交(源码)
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
@ -177,17 +189,17 @@ pnpm install && pnpm build
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
如需恢复到最新版本:`git checkout main && git pull`。
|
||||
如需返回最新版本:`git checkout main && git pull`。
|
||||
|
||||
## 如果你卡住了
|
||||
|
||||
- 再次运行 `openclaw doctor`,并仔细阅读输出内容。
|
||||
- 再次运行 `openclaw doctor`,并仔细阅读输出。
|
||||
- 对于源码检出上的 `openclaw update --channel dev`,更新器会在需要时自动引导安装 `pnpm`。如果你看到 pnpm/corepack 引导错误,请手动安装 `pnpm`(或重新启用 `corepack`),然后重新运行更新。
|
||||
- 查看:[故障排除](/zh-CN/gateway/troubleshooting)
|
||||
- 在 Discord 中提问:[https://discord.gg/clawd](https://discord.gg/clawd)
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [安装概览](/zh-CN/install) — 所有安装方式
|
||||
- [Install Overview](/zh-CN/install) — 所有安装方式
|
||||
- [Doctor](/zh-CN/gateway/doctor) — 更新后的健康检查
|
||||
- [迁移](/zh-CN/install/migrating) — 主要版本迁移指南
|
||||
- [Migrating](/zh-CN/install/migrating) — 主要版本迁移指南
|
||||
|
||||
@ -1,140 +0,0 @@
|
||||
---
|
||||
read_when:
|
||||
- 排查重复的节点 exec 完成事件
|
||||
- 处理 heartbeat / system-event 去重
|
||||
summary: 重复异步 exec 完成注入的调查记录
|
||||
title: Async Exec Duplicate Completion Investigation
|
||||
x-i18n:
|
||||
generated_at: "2026-04-24T04:06:23Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: e448cdcff6c799bf7f40caea2698c3293d1a78ed85ba5ffdfe10f53ce125f0ab
|
||||
source_path: refactor/async-exec-duplicate-completion-investigation.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
## 范围
|
||||
|
||||
- 会话:`agent:main:telegram:group:-1003774691294:topic:1`
|
||||
- 症状:同一个会话 / 运行 `keen-nexus` 的异步 exec 完成在 LCM 中被作为用户轮次记录了两次。
|
||||
- 目标:识别这更可能是重复会话注入,还是普通的出站投递重试。
|
||||
|
||||
## 结论
|
||||
|
||||
这更可能是**重复会话注入**,而不是单纯的出站投递重试。
|
||||
|
||||
Gateway 网关侧最明显的缺口在于**节点 exec 完成路径**:
|
||||
|
||||
1. 节点侧 exec 完成会发出带完整 `runId` 的 `exec.finished`。
|
||||
2. Gateway 网关的 `server-node-events` 会将其转换为系统事件并请求 heartbeat。
|
||||
3. heartbeat 运行会将已排空的系统事件块注入到智能体提示中。
|
||||
4. 内嵌运行器会将该提示作为新的用户轮次持久化到会话转录中。
|
||||
|
||||
如果出于任何原因(重放、重连重复、上游重发、生产者重复)导致同一个 `runId` 的 `exec.finished` 两次到达 Gateway 网关,OpenClaw 目前在这条路径上**没有基于 `runId` / `contextKey` 的幂等性检查**。第二份副本就会变成第二条内容相同的用户消息。
|
||||
|
||||
## 精确代码路径
|
||||
|
||||
### 1. 生产者:节点 exec 完成事件
|
||||
|
||||
- `src/node-host/invoke.ts:340-360`
|
||||
- `sendExecFinishedEvent(...)` 发出 `node.event`,事件为 `exec.finished`。
|
||||
- 负载包含 `sessionKey` 和完整 `runId`。
|
||||
|
||||
### 2. Gateway 网关事件摄取
|
||||
|
||||
- `src/gateway/server-node-events.ts:574-640`
|
||||
- 处理 `exec.finished`。
|
||||
- 构建文本:
|
||||
- `Exec finished (node=..., id=<runId>, code ...)`
|
||||
- 通过以下方式将其排入队列:
|
||||
- `enqueueSystemEvent(text, { sessionKey, contextKey: runId ? \`exec:${runId}\` : "exec", trusted: false })`
|
||||
- 然后立即请求唤醒:
|
||||
- `requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" }))`
|
||||
|
||||
### 3. 系统事件去重薄弱点
|
||||
|
||||
- `src/infra/system-events.ts:90-115`
|
||||
- `enqueueSystemEvent(...)` 只会抑制**连续的重复文本**:
|
||||
- `if (entry.lastText === cleaned) return false`
|
||||
- 它会存储 `contextKey`,但**不会**使用 `contextKey` 做幂等性检查。
|
||||
- drain 之后,重复抑制会重置。
|
||||
|
||||
这意味着,即使代码已经具备稳定的幂等性候选项(`exec:<runId>`),带相同 `runId` 的重放 `exec.finished` 仍然可能在稍后再次被接受。
|
||||
|
||||
### 4. 唤醒处理不是主要的重复来源
|
||||
|
||||
- `src/infra/heartbeat-wake.ts:79-117`
|
||||
- 唤醒会按 `(agentId, sessionKey)` 合并。
|
||||
- 相同目标的重复唤醒请求会折叠为一个待处理唤醒条目。
|
||||
|
||||
这使得**单独由重复唤醒处理**导致问题的解释,比重复事件摄取要弱。
|
||||
|
||||
### 5. Heartbeat 会消费该事件并将其变成提示输入
|
||||
|
||||
- `src/infra/heartbeat-runner.ts:535-574`
|
||||
- 预检查会窥视待处理系统事件,并对 exec-event 运行进行分类。
|
||||
- `src/auto-reply/reply/session-system-events.ts:86-90`
|
||||
- `drainFormattedSystemEvents(...)` 会排空该会话的队列。
|
||||
- `src/auto-reply/reply/get-reply-run.ts:400-427`
|
||||
- 排空后的系统事件块会被前置到智能体提示正文中。
|
||||
|
||||
### 6. 转录注入点
|
||||
|
||||
- `src/agents/pi-embedded-runner/run/attempt.ts:2000-2017`
|
||||
- `activeSession.prompt(effectivePrompt)` 会将完整提示提交给内嵌 PI 会话。
|
||||
- 这就是完成事件派生的提示被持久化为用户轮次的位置。
|
||||
|
||||
因此,一旦同一个系统事件被重建进提示两次,就会预期出现重复的 LCM 用户消息。
|
||||
|
||||
## 为什么单纯的出站投递重试可能性较低
|
||||
|
||||
Heartbeat 运行器中确实存在真实的出站失败路径:
|
||||
|
||||
- `src/infra/heartbeat-runner.ts:1194-1242`
|
||||
- 先生成回复。
|
||||
- 之后通过 `deliverOutboundPayloads(...)` 进行出站投递。
|
||||
- 此处失败会返回 `{ status: "failed" }`。
|
||||
|
||||
但是,对于同一个系统事件队列条目,仅凭这一点**不足以**解释重复的用户轮次:
|
||||
|
||||
- `src/auto-reply/reply/session-system-events.ts:86-90`
|
||||
- 系统事件队列在出站投递之前就已经被排空。
|
||||
|
||||
因此,仅凭渠道发送重试本身,不会重新创建完全相同的排队事件。它可以解释外部投递缺失 / 失败,但不足以单独解释第二条完全相同的会话用户消息。
|
||||
|
||||
## 次要的、置信度较低的可能性
|
||||
|
||||
智能体运行器中存在完整运行重试循环:
|
||||
|
||||
- `src/auto-reply/reply/agent-runner-execution.ts:741-1473`
|
||||
- 某些瞬时失败会重试整个运行,并重新提交相同的 `commandBody`。
|
||||
|
||||
如果触发重试条件之前提示已经追加进去,那么这会在**同一次回复执行内部**复制持久化的用户提示。
|
||||
|
||||
我将这一可能性排在重复 `exec.finished` 摄取之后,因为:
|
||||
|
||||
- 观察到的时间间隔大约为 51 秒,这看起来更像是第二次唤醒 / 轮次,而不是进程内重试;
|
||||
- 报告已经提到重复的消息发送失败,这更指向一个独立的后续轮次,而不是即时的模型 / 运行时重试。
|
||||
|
||||
## 根因假设
|
||||
|
||||
最高置信度的假设:
|
||||
|
||||
- `keen-nexus` 完成事件来自**节点 exec 事件路径**。
|
||||
- 相同的 `exec.finished` 被两次投递到 `server-node-events`。
|
||||
- Gateway 网关接受了这两次事件,因为 `enqueueSystemEvent(...)` 不会按 `contextKey` / `runId` 去重。
|
||||
- 每个被接受的事件都会触发 heartbeat,并作为用户轮次注入到 PI 转录中。
|
||||
|
||||
## 建议的微小外科式修复
|
||||
|
||||
如果需要修复,最小且高价值的改动是:
|
||||
|
||||
- 让 exec / system-event 幂等性在短时间窗口内遵循 `contextKey`,至少对精确的 `(sessionKey, contextKey, text)` 重复项生效;
|
||||
- 或者在 `server-node-events` 中为 `exec.finished` 添加一个专用去重,键为 `(sessionKey, runId, event kind)`。
|
||||
|
||||
这样就能在重放的 `exec.finished` 变成会话轮次之前,直接阻止重复。
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [Exec 工具](/zh-CN/tools/exec)
|
||||
- [Session management](/zh-CN/concepts/session)
|
||||
@ -1,545 +0,0 @@
|
||||
---
|
||||
read_when:
|
||||
- 重构 QA 场景定义或 qa-lab harness 代码
|
||||
- 在 Markdown 场景与 TypeScript harness 逻辑之间迁移 QA 行为
|
||||
summary: QA 重构计划:场景目录与 harness 整合
|
||||
title: QA 重构
|
||||
x-i18n:
|
||||
generated_at: "2026-04-24T04:20:56Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 0d774d7b5e0fffd5c2504d9a4d6063198d77b866263ea8448474dce6246012d4
|
||||
source_path: refactor/qa.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
状态:基础迁移已落地。
|
||||
|
||||
## 目标
|
||||
|
||||
将 OpenClaw QA 从“定义分散”的模式迁移为单一事实来源:
|
||||
|
||||
- 场景元数据
|
||||
- 发送给模型的提示词
|
||||
- 设置与清理
|
||||
- harness 逻辑
|
||||
- 断言与成功标准
|
||||
- 产物与报告提示
|
||||
|
||||
期望的最终状态是:一个通用的 QA harness 加载功能强大的场景定义文件,而不是在 TypeScript 中硬编码大多数行为。
|
||||
|
||||
## 当前状态
|
||||
|
||||
主要事实来源现在位于 `qa/scenarios/index.md`,以及每个场景对应的 `qa/scenarios/<theme>/*.md` 文件中。
|
||||
|
||||
已实现:
|
||||
|
||||
- `qa/scenarios/index.md`
|
||||
- 规范的 QA 包元数据
|
||||
- 操作员身份
|
||||
- 启动任务
|
||||
- `qa/scenarios/<theme>/*.md`
|
||||
- 每个场景一个 Markdown 文件
|
||||
- 场景元数据
|
||||
- handler 绑定
|
||||
- 场景特定执行配置
|
||||
- `extensions/qa-lab/src/scenario-catalog.ts`
|
||||
- Markdown 包解析器 + zod 校验
|
||||
- `extensions/qa-lab/src/qa-agent-bootstrap.ts`
|
||||
- 基于 Markdown 包渲染计划
|
||||
- `extensions/qa-lab/src/qa-agent-workspace.ts`
|
||||
- 生成兼容性文件以及 `QA_SCENARIOS.md`
|
||||
- `extensions/qa-lab/src/suite.ts`
|
||||
- 通过 Markdown 定义的 handler 绑定选择可执行场景
|
||||
- QA bus 协议 + UI
|
||||
- 用于图片/视频/音频/文件渲染的通用内联附件
|
||||
|
||||
仍然分散的表面:
|
||||
|
||||
- `extensions/qa-lab/src/suite.ts`
|
||||
- 仍然拥有大多数可执行的自定义 handler 逻辑
|
||||
- `extensions/qa-lab/src/report.ts`
|
||||
- 仍然从运行时输出推导报告结构
|
||||
|
||||
因此,事实来源分裂的问题已经修复,但执行仍然主要依赖 handler,而不是完全声明式。
|
||||
|
||||
## 实际的场景表面是什么样
|
||||
|
||||
阅读当前 suite 可以看出几类不同的场景。
|
||||
|
||||
### 简单交互
|
||||
|
||||
- 渠道基线
|
||||
- 私信基线
|
||||
- 线程跟进
|
||||
- 模型切换
|
||||
- 审批后续执行
|
||||
- 反应/编辑/删除
|
||||
|
||||
### 配置与运行时变更
|
||||
|
||||
- 配置补丁禁用 skill
|
||||
- 配置应用后重启唤醒
|
||||
- 配置重启能力切换
|
||||
- 运行时清单漂移检查
|
||||
|
||||
### 文件系统与代码仓库断言
|
||||
|
||||
- source/docs 发现报告
|
||||
- 构建 Lobster Invaders
|
||||
- 生成图片产物查找
|
||||
|
||||
### Memory 编排
|
||||
|
||||
- Memory 召回
|
||||
- 渠道上下文中的 Memory 工具
|
||||
- Memory 失败回退
|
||||
- 会话 Memory 排序
|
||||
- 线程 Memory 隔离
|
||||
- Memory Dreaming sweep
|
||||
|
||||
### 工具与插件集成
|
||||
|
||||
- MCP plugin-tools 调用
|
||||
- skill 可见性
|
||||
- skill 热安装
|
||||
- 原生图片生成
|
||||
- 图片往返
|
||||
- 基于附件的图片理解
|
||||
|
||||
### 多轮与多参与者
|
||||
|
||||
- subagent 交接
|
||||
- subagent 扇出综合
|
||||
- 重启恢复类流程
|
||||
|
||||
这些分类很重要,因为它们决定 DSL 的需求。单纯的“提示词 + 期望文本”平铺列表是不够的。
|
||||
|
||||
## 方向
|
||||
|
||||
### 单一事实来源
|
||||
|
||||
使用 `qa/scenarios/index.md` 与 `qa/scenarios/<theme>/*.md` 作为编写时的事实来源。
|
||||
|
||||
这个包应保持:
|
||||
|
||||
- 在代码评审中易于阅读
|
||||
- 可被机器解析
|
||||
- 足够丰富,能够驱动:
|
||||
- suite 执行
|
||||
- QA 工作区 bootstrap
|
||||
- QA Lab UI 元数据
|
||||
- docs/discovery 提示词
|
||||
- 报告生成
|
||||
|
||||
### 首选编写格式
|
||||
|
||||
使用 Markdown 作为顶层格式,并在其中嵌入结构化 YAML。
|
||||
|
||||
推荐形态:
|
||||
|
||||
- YAML frontmatter
|
||||
- id
|
||||
- title
|
||||
- surface
|
||||
- tags
|
||||
- docs refs
|
||||
- code refs
|
||||
- model/provider overrides
|
||||
- prerequisites
|
||||
- prose sections
|
||||
- objective
|
||||
- notes
|
||||
- debugging hints
|
||||
- fenced YAML blocks
|
||||
- setup
|
||||
- steps
|
||||
- assertions
|
||||
- cleanup
|
||||
|
||||
这样可以获得:
|
||||
|
||||
- 比大型 JSON 更好的 PR 可读性
|
||||
- 比纯 YAML 更丰富的上下文
|
||||
- 严格解析与 zod 校验
|
||||
|
||||
原始 JSON 仅可作为中间生成格式接受。
|
||||
|
||||
## 建议的场景文件形态
|
||||
|
||||
示例:
|
||||
|
||||
````md
|
||||
---
|
||||
id: image-generation-roundtrip
|
||||
title: Image generation roundtrip
|
||||
surface: image
|
||||
tags: [media, image, roundtrip]
|
||||
models:
|
||||
primary: openai/gpt-5.4
|
||||
requires:
|
||||
tools: [image_generate]
|
||||
plugins: [openai, qa-channel]
|
||||
docsRefs:
|
||||
- docs/help/testing.md
|
||||
- docs/concepts/model-providers.md
|
||||
codeRefs:
|
||||
- extensions/qa-lab/src/suite.ts
|
||||
- src/gateway/chat-attachments.ts
|
||||
---
|
||||
|
||||
# Objective
|
||||
|
||||
Verify generated media is reattached on the follow-up turn.
|
||||
|
||||
# Setup
|
||||
|
||||
```yaml scenario.setup
|
||||
- action: config.patch
|
||||
patch:
|
||||
agents:
|
||||
defaults:
|
||||
imageGenerationModel:
|
||||
primary: openai/gpt-image-1
|
||||
- action: session.create
|
||||
key: agent:qa:image-roundtrip
|
||||
```
|
||||
|
||||
# Steps
|
||||
|
||||
```yaml scenario.steps
|
||||
- action: agent.send
|
||||
session: agent:qa:image-roundtrip
|
||||
message: |
|
||||
Image generation check: generate a QA lighthouse image and summarize it in one short sentence.
|
||||
- action: artifact.capture
|
||||
kind: generated-image
|
||||
promptSnippet: Image generation check
|
||||
saveAs: lighthouseImage
|
||||
- action: agent.send
|
||||
session: agent:qa:image-roundtrip
|
||||
message: |
|
||||
Roundtrip image inspection check: describe the generated lighthouse attachment in one short sentence.
|
||||
attachments:
|
||||
- fromArtifact: lighthouseImage
|
||||
```
|
||||
|
||||
# Expect
|
||||
|
||||
```yaml scenario.expect
|
||||
- assert: outbound.textIncludes
|
||||
value: lighthouse
|
||||
- assert: requestLog.matches
|
||||
where:
|
||||
promptIncludes: Roundtrip image inspection check
|
||||
imageInputCountGte: 1
|
||||
- assert: artifact.exists
|
||||
ref: lighthouseImage
|
||||
```
|
||||
````
|
||||
|
||||
## DSL 必须覆盖的 runner 能力
|
||||
|
||||
根据当前 suite,通用 runner 需要的不只是提示词执行。
|
||||
|
||||
### 环境与设置动作
|
||||
|
||||
- `bus.reset`
|
||||
- `gateway.waitHealthy`
|
||||
- `channel.waitReady`
|
||||
- `session.create`
|
||||
- `thread.create`
|
||||
- `workspace.writeSkill`
|
||||
|
||||
### 智能体轮次动作
|
||||
|
||||
- `agent.send`
|
||||
- `agent.wait`
|
||||
- `bus.injectInbound`
|
||||
- `bus.injectOutbound`
|
||||
|
||||
### 配置与运行时动作
|
||||
|
||||
- `config.get`
|
||||
- `config.patch`
|
||||
- `config.apply`
|
||||
- `gateway.restart`
|
||||
- `tools.effective`
|
||||
- `skills.status`
|
||||
|
||||
### 文件与产物动作
|
||||
|
||||
- `file.write`
|
||||
- `file.read`
|
||||
- `file.delete`
|
||||
- `file.touchTime`
|
||||
- `artifact.captureGeneratedImage`
|
||||
- `artifact.capturePath`
|
||||
|
||||
### Memory 与 cron 动作
|
||||
|
||||
- `memory.indexForce`
|
||||
- `memory.searchCli`
|
||||
- `doctor.memory.status`
|
||||
- `cron.list`
|
||||
- `cron.run`
|
||||
- `cron.waitCompletion`
|
||||
- `sessionTranscript.write`
|
||||
|
||||
### MCP 动作
|
||||
|
||||
- `mcp.callTool`
|
||||
|
||||
### 断言
|
||||
|
||||
- `outbound.textIncludes`
|
||||
- `outbound.inThread`
|
||||
- `outbound.notInRoot`
|
||||
- `tool.called`
|
||||
- `tool.notPresent`
|
||||
- `skill.visible`
|
||||
- `skill.disabled`
|
||||
- `file.contains`
|
||||
- `memory.contains`
|
||||
- `requestLog.matches`
|
||||
- `sessionStore.matches`
|
||||
- `cron.managedPresent`
|
||||
- `artifact.exists`
|
||||
|
||||
## 变量与产物引用
|
||||
|
||||
DSL 必须支持保存输出并在后续引用。
|
||||
|
||||
当前 suite 中的示例:
|
||||
|
||||
- 创建一个线程,然后复用 `threadId`
|
||||
- 创建一个会话,然后复用 `sessionKey`
|
||||
- 生成一张图片,然后在下一轮中附加该文件
|
||||
- 生成一个唤醒标记字符串,然后断言它稍后出现
|
||||
|
||||
所需能力:
|
||||
|
||||
- `saveAs`
|
||||
- `${vars.name}`
|
||||
- `${artifacts.name}`
|
||||
- 针对路径、会话键、线程 id、标记、工具输出的类型化引用
|
||||
|
||||
如果没有变量支持,harness 逻辑就会继续从场景泄漏回 TypeScript。
|
||||
|
||||
## 哪些部分应保留为逃生舱
|
||||
|
||||
在第 1 阶段,实现一个完全纯声明式的 runner 并不现实。
|
||||
|
||||
有些场景天然就需要大量编排:
|
||||
|
||||
- Memory Dreaming sweep
|
||||
- 配置应用后重启唤醒
|
||||
- 配置重启能力切换
|
||||
- 基于时间戳/路径的生成图片产物解析
|
||||
- discovery-report 评估
|
||||
|
||||
目前这些场景应继续使用显式自定义 handler。
|
||||
|
||||
推荐规则:
|
||||
|
||||
- 85-90% 声明式
|
||||
- 对于剩余较难的部分,使用显式 `customHandler` 步骤
|
||||
- 仅允许具名且有文档的自定义 handler
|
||||
- 场景文件中不允许匿名内联代码
|
||||
|
||||
这样既能保持通用引擎整洁,又能继续推进。
|
||||
|
||||
## 架构变更
|
||||
|
||||
### 当前
|
||||
|
||||
场景 Markdown 已经是以下内容的事实来源:
|
||||
|
||||
- suite 执行
|
||||
- 工作区 bootstrap 文件
|
||||
- QA Lab UI 场景目录
|
||||
- 报告元数据
|
||||
- discovery 提示词
|
||||
|
||||
生成的兼容性内容:
|
||||
|
||||
- 种子工作区仍然包含 `QA_KICKOFF_TASK.md`
|
||||
- 种子工作区仍然包含 `QA_SCENARIO_PLAN.md`
|
||||
- 种子工作区现在也包含 `QA_SCENARIOS.md`
|
||||
|
||||
## 重构计划
|
||||
|
||||
### 第 1 阶段:加载器与 schema
|
||||
|
||||
已完成。
|
||||
|
||||
- 添加了 `qa/scenarios/index.md`
|
||||
- 将场景拆分到 `qa/scenarios/<theme>/*.md`
|
||||
- 为具名 Markdown YAML 包内容添加了解析器
|
||||
- 使用 zod 进行校验
|
||||
- 将消费者切换到解析后的包
|
||||
- 移除了仓库级的 `qa/seed-scenarios.json` 和 `qa/QA_KICKOFF_TASK.md`
|
||||
|
||||
### 第 2 阶段:通用引擎
|
||||
|
||||
- 将 `extensions/qa-lab/src/suite.ts` 拆分为:
|
||||
- loader
|
||||
- engine
|
||||
- action registry
|
||||
- assertion registry
|
||||
- custom handlers
|
||||
- 保留现有辅助函数作为引擎操作
|
||||
|
||||
交付物:
|
||||
|
||||
- 引擎可执行简单的声明式场景
|
||||
|
||||
从主要是“提示词 + 等待 + 断言”的场景开始:
|
||||
|
||||
- 线程跟进
|
||||
- 基于附件的图片理解
|
||||
- skill 可见性与调用
|
||||
- 渠道基线
|
||||
|
||||
交付物:
|
||||
|
||||
- 第一批真正由 Markdown 定义并通过通用引擎运行的场景上线
|
||||
|
||||
### 第 4 阶段:迁移中等复杂度场景
|
||||
|
||||
- 图片生成往返
|
||||
- 渠道上下文中的 Memory 工具
|
||||
- 会话 Memory 排序
|
||||
- subagent 交接
|
||||
- subagent 扇出综合
|
||||
|
||||
交付物:
|
||||
|
||||
- 变量、产物、工具断言、request-log 断言已被验证
|
||||
|
||||
### 第 5 阶段:保留困难场景为自定义 handler
|
||||
|
||||
- Memory Dreaming sweep
|
||||
- 配置应用后重启唤醒
|
||||
- 配置重启能力切换
|
||||
- 运行时清单漂移
|
||||
|
||||
交付物:
|
||||
|
||||
- 相同的编写格式,但在需要时带有显式 custom-step 块
|
||||
|
||||
### 第 6 阶段:删除硬编码场景映射
|
||||
|
||||
一旦包覆盖率足够高:
|
||||
|
||||
- 删除 `extensions/qa-lab/src/suite.ts` 中大多数按场景分支的 TypeScript 逻辑
|
||||
|
||||
## Fake Slack / 富媒体支持
|
||||
|
||||
当前的 QA bus 以文本优先。
|
||||
|
||||
相关文件:
|
||||
|
||||
- `extensions/qa-channel/src/protocol.ts`
|
||||
- `extensions/qa-lab/src/bus-state.ts`
|
||||
- `extensions/qa-lab/src/bus-queries.ts`
|
||||
- `extensions/qa-lab/src/bus-server.ts`
|
||||
- `extensions/qa-lab/web/src/ui-render.ts`
|
||||
|
||||
目前 QA bus 支持:
|
||||
|
||||
- 文本
|
||||
- 反应
|
||||
- 线程
|
||||
|
||||
它还不能对内联媒体附件建模。
|
||||
|
||||
### 所需传输契约
|
||||
|
||||
添加一个通用的 QA bus 附件模型:
|
||||
|
||||
```ts
|
||||
type QaBusAttachment = {
|
||||
id: string;
|
||||
kind: "image" | "video" | "audio" | "file";
|
||||
mimeType: string;
|
||||
fileName?: string;
|
||||
inline?: boolean;
|
||||
url?: string;
|
||||
contentBase64?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
durationMs?: number;
|
||||
altText?: string;
|
||||
transcript?: string;
|
||||
};
|
||||
```
|
||||
|
||||
然后将 `attachments?: QaBusAttachment[]` 添加到:
|
||||
|
||||
- `QaBusMessage`
|
||||
- `QaBusInboundMessageInput`
|
||||
- `QaBusOutboundMessageInput`
|
||||
|
||||
### 为什么先做通用模型
|
||||
|
||||
不要构建仅适用于 Slack 的媒体模型。
|
||||
|
||||
而应该采用:
|
||||
|
||||
- 一个通用的 QA 传输模型
|
||||
- 在其上构建多个渲染器
|
||||
- 当前 QA Lab 聊天视图
|
||||
- 未来的 fake Slack web
|
||||
- 任何其他 fake transport 视图
|
||||
|
||||
这样可以避免重复逻辑,并让媒体场景保持传输无关。
|
||||
|
||||
### 所需 UI 工作
|
||||
|
||||
更新 QA UI,以渲染:
|
||||
|
||||
- 内联图片预览
|
||||
- 内联音频播放器
|
||||
- 内联视频播放器
|
||||
- 文件附件 chip
|
||||
|
||||
当前 UI 已经可以渲染线程与反应,因此附件渲染应当能够叠加到同一套消息卡片模型上。
|
||||
|
||||
### 媒体传输启用后的场景工作
|
||||
|
||||
一旦附件可以通过 QA bus 流转,就可以添加更丰富的 fake-chat 场景:
|
||||
|
||||
- fake Slack 中的内联图片回复
|
||||
- 音频附件理解
|
||||
- 视频附件理解
|
||||
- 混合附件排序
|
||||
- 保留媒体的线程回复
|
||||
|
||||
## 建议
|
||||
|
||||
下一块实现工作应当是:
|
||||
|
||||
1. 添加 Markdown 场景加载器 + zod schema
|
||||
2. 从 Markdown 生成当前目录
|
||||
3. 先迁移几个简单场景
|
||||
4. 添加通用 QA bus 附件支持
|
||||
5. 在 QA UI 中渲染内联图片
|
||||
6. 然后扩展到音频和视频
|
||||
|
||||
这是能够同时证明两个目标的最小路径:
|
||||
|
||||
- 通用的、由 Markdown 定义的 QA
|
||||
- 更丰富的 fake messaging surfaces
|
||||
|
||||
## 未决问题
|
||||
|
||||
- 场景文件是否应允许嵌入带变量插值的 Markdown 提示词模板
|
||||
- 设置/清理应当是具名 section,还是仅作为有序动作列表
|
||||
- 产物引用在 schema 中应采用强类型,还是基于字符串
|
||||
- 自定义 handler 应集中放在一个 registry 中,还是按 surface 分 registry
|
||||
- 在迁移期间,生成的 JSON 兼容文件是否应继续检入
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [QA E2E 自动化](/zh-CN/concepts/qa-e2e-automation)
|
||||
Loading…
Reference in New Issue
Block a user