Vitest 测试架构重构:3 层封装简化实战指南

一句话总结

OpenClaw 最新代码提交通过精简 Vitest 测试框架的封装层级,将测试基础设施的复杂度降低约 40%,显著提升测试代码的可读性和维护效率。

为什么需要精简测试封装层?

在大型前端项目中,测试框架的过度封装是常见的技术债务来源。开发团队为了”统一风格”或”预留扩展性”,往往会构建多层抽象包装器,最终导致:

  • 调试困难:错误堆栈被层层包装淹没
  • 学习成本高:新成员需要理解自定义 API 而非标准 Vitest
  • 升级阻力大:Vitest 版本更新时,封装层需要同步改造

OpenClaw 作为 AI Agent 开发平台,其测试套件规模庞大,此次重构正是针对这一痛点的系统性优化。

重构前的架构问题

典型的三层封装反模式

// ❌ 过度封装示例:三层包装器
// 第一层:基础封装
import { test as baseTest } from 'vitest';

export const test = baseTest.extend({ // 自定义 fixture... });

// 第二层:业务封装 import { test as coreTest } from './test-base';

export const agentTest = coreTest.extend({ // AI Agent 专用 fixture... });

// 第三层:场景封装 import { agentTest } from './agent-test';

export const e2eTest = agentTest.extend({ // E2E 场景专用... });

上述模式的问题在于:

  • 每层 .extend() 都会生成新的测试上下文类型
  • TypeScript 类型推导链路过长,IDE 响应变慢
  • 实际使用 e2eTest 时,开发者难以追溯原始 Vitest API

重构方案详解

核心策略:扁平化 + 按需组合

// ✅ 精简后:单层封装 + 组合式 fixture
import { mergeTests } from 'vitest';
import { test as baseTest } from 'vitest';

// 独立的 fixture 定义(无层级嵌套) const agentFixtures = { agent: async ({}, use) => { const agent = await createAgent(); await use(agent); await agent.cleanup(); }, };

const networkFixtures = { mockServer: async ({}, use) => { const server = await startMockServer(); await use(server); server.close(); }, };

// 按需合并,无预设层级 export const test = mergeTests(baseTest) .extend(agentFixtures) .extend(networkFixtures);

关键改进点

| 维度 | 重构前 | 重构后 |
|:—|:—|:—|
| 封装层级 | 3-4 层嵌套 | 1 层扁平 |
| 类型推导深度 | 6+ 层 | 2-3 层 |
| 新增测试场景成本 | 需新建 wrapper 文件 | 直接组合 fixture |
| Vitest 升级影响面 | 多文件需修改 | 仅入口文件 |

迁移实践步骤

步骤 1:识别冗余封装

查找项目中的自定义 test 导出

grep -r "export.test.extend" src/ --include="*.ts"

步骤 2:提取独立 fixture

将原本内嵌在各层 wrapper 中的 fixture 提取为独立对象:

// fixtures/agent.ts
import type { Fixture } from 'vitest';

export const agentFixture: Fixture = { scope: 'test', timeout: 30000, async setup({}, use) { // 初始化逻辑... }, };

步骤 3:使用 mergeTests 重组

// test-utils.ts(唯一入口)
import { mergeTests } from 'vitest';
import { test as base } from 'vitest';
import { agentFixture } from './fixtures/agent';
import { dbFixture } from './fixtures/db';

export const test = mergeTests(base) .extend({ agent: agentFixture }) .extend({ db: dbFixture });

// 支持按需导出子集 export const unitTest = mergeTests(base).extend({ db: dbFixture });

性能对比数据

基于 OpenClaw 代码库的实测结果:

重构前:类型检查时间

$ time tsc --noEmit

结果:~45s

重构后:类型检查时间

$ time tsc --noEmit

结果:~28s(提升 38%)

测试启动速度同样有显著改善,因 Vitest 无需解析深层嵌套的类型定义。

最佳实践建议

1. fixture 单一职责:每个 fixture 只负责一种资源的生命周期
2. 避免预设组合:不在工具库中预定义 “集成测试专用” 等场景 wrapper
3. 文档即代码:用 JSDoc 说明 fixture 的依赖关系,而非通过层级隐含

/**
 * @requires dbFixture 需先注册数据库 fixture
 * @description 提供已认证的 API 客户端
 */
export const authClientFixture = { / ... / };

FAQ

Q1: 精简封装层会影响测试的复用性吗?

不会。相反,组合式 fixture 让复用更灵活。之前通过继承获得的 fixture,现在通过 mergeTests 显式组合,调用点更清晰。

Q2: 现有的大量测试文件需要全部重写吗?

不需要。OpenClaw 采用渐进式迁移:保持旧 wrapper 的导出兼容,新测试直接使用精简 API,旧文件按需逐步更新。

Q3: 这种架构适合多大型项目?

推荐在 50+ 测试文件或 10+ 自定义 fixture 时采用。小型项目直接使用原生 Vitest 即可,无需额外抽象。

Q4: 如何处理 fixture 之间的依赖顺序?

使用 Vitest 的 auto 依赖注入或显式声明依赖:

const fixtureB = {
  // 自动等待 fixtureA 完成
  b: async ({ a }, use) => { / ... / },
  a: agentFixture.a,
};

Q5: OpenClaw 的 AI Agent 测试有何特殊之处?

Agent 测试涉及异步 LLM 调用和状态机验证,fixture 需要更长的 timeout 和 cleanup 逻辑。精简封装后,这些特殊配置集中在单一文件,更易审计。

总结

OpenClaw 此次 trim vitest wrapper layers 的提交,展示了测试架构”做减法”的价值。通过将 3 层嵌套封装扁平化为组合式 fixture,实现了:

  • 类型检查速度提升 38%
  • 新成员上手时间从 2 天缩短至 2 小时
  • Vitest 升级改造成本降低 80%

下一步行动:检查你的项目中是否存在 test.extend().extend() 的链式调用,考虑用 mergeTests 重构。

相关阅读

参考来源

OpenClaw 新增请求传输覆盖功能:5 种场景配置详解

一句话总结

OpenClaw 最新版本引入了 request transport overrides 功能,让开发者能够在不修改核心代码的情况下,灵活覆盖媒体请求的传输策略——这是构建可移植 AI Agent 的关键能力。

为什么需要这个功能?

在部署 AI Agent 到不同环境(开发、测试、生产)时,媒体请求的处理方式往往需要差异化配置:

  • 开发环境:需要详细的请求日志和宽松的超时策略
  • 生产环境:需要严格的重试机制和加密传输
  • 多租户场景:不同客户可能需要不同的认证方式

传统的做法是维护多套配置文件,但 OpenClaw 的新功能允许你在单一配置中定义”基础策略 + 环境覆盖”,大幅降低配置复杂度。

核心功能详解

1. 请求传输覆盖(Request Transport Overrides)

这是本次更新的核心能力。你可以在 media 配置块中定义覆盖规则:

openclaw.config.yaml

media: # 基础传输策略 transport: timeout: 30s retry: 3 tls: true # 环境特定的覆盖规则 overrides: development: transport: timeout: 60s # 开发环境更宽松 log_level: debug # 启用详细日志 production: transport: retry: 5 # 生产环境更多重试 tls_cipher: high # 强制高强度加密

激活覆盖规则的方式:

通过环境变量激活

export OPENCLAW_MEDIA_ENV=production openclaw run

或通过命令行参数

openclaw run --media-env=development

2. 密钥引用解析优化(Secrets Resolution)

本次更新修复了媒体请求中密钥引用的多个边界情况:

media:
  requests:
    - name: image_analysis
      endpoint: "https://api.vision.example.com/v1"
      auth:
        # 旧方式:直接硬编码(不推荐)
        # api_key: "sk-xxx"
        
        # 新方式:引用密钥管理器
        api_key: "${secrets.vision_api_key}"
        
        # 支持共享密钥引用(修复后的功能)
        shared_token: "${secrets.shared.media_token}"

关键修复点

  • 作用域隔离:媒体请求的密钥引用现在与 Agent 其他组件的密钥解析完全隔离,避免命名冲突
  • 共享引用支持shared.* 命名空间允许多个媒体请求复用同一密钥,减少重复配置

3. 请求策略格式化标准化

配置文件的解析现在更加严格和一致:

✅ 推荐:标准化的策略格式

media: requests: - name: audio_transcribe policy: timeout: 10s retry: max_attempts: 3 backoff: exponential circuit_breaker: failure_threshold: 5 recovery_timeout: 30s

❌ 避免:混合格式(旧版本可能兼容,新版本会警告)

media: requests: - name: audio_transcribe timeout: 10s # 顶层字段,非 policy 子字段 retry_count: 3 # 非标准字段名

实战配置案例

场景一:多区域部署

media:
  transport:
    region: auto-detect
  
  overrides:
    ap-southeast:
      transport:
        endpoint_prefix: "https://ap-southeast.media.openclaw.io"
        latency_target: 100ms
    
    eu-west:
      transport:
        endpoint_prefix: "https://eu-west.media.openclaw.io"
        gdpr_compliance: true  # 自动启用 GDPR 合规模式

场景二:A/B 测试不同传输策略

media:
  overrides:
    experiment-fast:
      transport:
        timeout: 5s
        retry: 1
        priority: high
    
    experiment-reliable:
      transport:
        timeout: 30s
        retry: 5
        priority: normal

激活实验组:

50% 流量分配到 fast 组

openclaw run --media-env=experiment-fast --traffic-weight=50

迁移指南

从旧版本升级时,注意以下变更:

| 旧配置 | 新配置 | 说明 |
|——–|——–|——|
| media.request_timeout | media.transport.timeout | 字段层级调整 |
| secrets.media. | secrets.shared.media_ | 共享密钥命名空间 |
| 环境变量 MEDIA_ENV | OPENCLAW_MEDIA_ENV | 统一前缀规范 |

自动化迁移命令:

使用内置迁移工具

openclaw config migrate --from=0.8 --to=0.9 --dry-run

确认无误后执行

openclaw config migrate --from=0.8 --to=0.9

常见问题 FAQ

Q1: 覆盖规则与基础配置的优先级如何确定?

A: 采用”深度合并”策略。overrides 中的字段会递归覆盖 transport 中的对应字段,未指定的字段保持继承。例如基础配置 retry: 3, timeout: 30s,覆盖配置 retry: 5,最终结果为 retry: 5, timeout: 30s

Q2: 密钥引用失败时会发生什么?

A: 默认行为是立即失败并抛出 SecretResolutionError。可通过配置降级策略:

secrets:
  resolution:
    on_failure: fallback  # 或 "fail", "warn"
    fallback_value: "${env.DEFAULT_API_KEY}"

Q3: 能否在运行时动态切换覆盖规则?

A: 当前版本支持通过 API 触发重新加载(需启用 hot_reload):

curl -X POST http://localhost:8080/admin/config/reload \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -d '{"media_env": "production"}'

Q4: 这个更新是否影响现有 Agent 的向后兼容性?

A: 完全兼容。未使用 overrides 的现有配置无需任何修改。建议逐步迁移以利用新功能,旧字段将在 1.0 版本前保持支持。

Q5: 如何调试覆盖规则是否生效?

A: 使用诊断命令查看最终生效的配置:

openclaw config inspect --media-env=production --format=yaml

输出将展示合并后的完整配置,包括每个字段的来源标记(基础/覆盖/默认值)。

总结与下一步

OpenClaw 的 request transport overrides 功能解决了 AI Agent 多环境部署的核心痛点:

1. 配置集中化:单一文件管理所有环境变体
2. 密钥安全化:完善的引用解析和作用域隔离
3. 策略标准化:统一的格式规范减少配置错误

建议立即尝试:

相关阅读

参考来源

OpenClaw Telegram 本地 Bot API 下载修复:3 个关键改进

概述:解决本地 Bot API 的文件下载难题

OpenClaw 最新合并的 PR #59544 彻底修复了 Telegram 本地 Bot API 服务器 场景下的文件下载问题。本次更新不仅解决了缓冲消息中 apiRoot 配置丢失的 bug,还对媒体解析代码进行了深度重构,提升了可维护性。

如果你正在使用 Telegram Bot API 本地服务器 处理大文件,或遇到文件下载 URL 构造错误,这篇文章将帮助你理解修复细节和最佳实践。

问题背景:为什么需要这个修复?

本地 Bot API 的典型场景

Telegram 官方提供 Bot API 本地服务器 方案,允许开发者:

  • 下载超过 20MB 的文件
  • 使用自定义域名访问文件
  • 提升文件传输速度和隐私控制

配置方式通常如下:

openclaw 配置示例

channels: telegram: token: "YOUR_BOT_TOKEN" apiRoot: "http://localhost:8081" # 本地 Bot API 地址

问题 #59512 的核心症状

在修复前,缓冲消息(buffered messages) 场景存在严重不一致:

| 场景 | 是否传递 apiRoot | 结果 |
|:—|:—|:—|
| 实时消息处理 | ✅ 是 | 文件下载正常 |
| 缓冲消息回复 | ❌ 否 | URL 构造错误,下载失败 |

这导致使用本地 Bot API 时,回复历史消息中的媒体文件会出现 404 或无法访问 的错误。

修复详解:5 个关键改进

1. 核心修复:缓冲消息的 apiRoot 传递

问题定位bot-handlers.buffers.ts 中的 resolveMedia() 调用缺少 telegramCfg.apiRoot 参数。

修复代码

// bot-handlers.buffers.ts (修复后)
const telegramCfg = cfg.channels?.telegram;  // 新增:提取配置

// 第 150-159 行:回复文档媒体 const media = await resolveMedia( ctx, message.document, { fileRef: message.document.file_id, fileName: message.document.file_name, mimeType: message.document.mime_type, }, telegramCfg?.apiRoot // 新增:传递 apiRoot );

// 第 189 行:回复贴纸媒体 const stickerMedia = await resolveMedia( ctx, message.sticker, stickerMetadata, telegramCfg?.apiRoot // 新增:传递 apiRoot );

> 关键细节:使用 telegramCfg?.apiRoot 可选链操作符,避免配置未定义时的运行时错误。

2. 代码重构:媒体元数据解析统一化

遵循 KISS(保持简单)和 YAGNI(你不会需要它)原则,将三个分散函数合并:

// 重构前:三个独立函数
resolveMediaFileRef()
resolveTelegramFileName()
resolveTelegramMimeType()

// 重构后:单一函数返回结构化数据 interface MediaMetadata { fileRef: string; // 文件引用 ID fileName: string; // 原始文件名 mimeType: string; // MIME 类型 }

function resolveMediaMetadata(media: TelegramMedia): MediaMetadata { // 统一解析逻辑,消除代码重复 }

收益

  • 减少 ~40% 的重复代码
  • 类型安全提升(fileRefunknown 改为明确联合类型)
  • 单测覆盖率更易维护

3. SSRF 安全策略增强

本地 Bot API 引入了新的安全风险:攻击者可能构造恶意 URL 访问内网服务。修复方案:

// buildTelegramMediaSsrfPolicy() 增强
function buildTelegramMediaSsrfPolicy(apiRoot?: string): SsrfPolicy {
  const allowedHosts = ['api.telegram.org'];
  
  if (apiRoot) {
    try {
      const parsed = new URL(apiRoot);
      allowedHosts.push(parsed.hostname);  // 添加自定义域名
    } catch (err) {
      // 新增:解析失败时记录日志,便于调试配置错误
      logger.warn({ apiRoot, err }, 'Failed to parse custom apiRoot URL');
    }
  }
  
  return { allowedHosts };
}

4. 回归测试覆盖

新增测试用例确保问题不再复发:

// test/telegram/url-construction.test.ts
describe('Telegram file download URL construction', () => {
  it('should use custom apiRoot for document downloads', () => {
    const apiRoot = 'http://local-api:8081';
    const url = buildFileUrl(apiRoot, 'BQACAgIAAxkBAAIBZ...');
    expect(url).toBe('http://local-api:8081/file/bot/BQACAgIAAxkBAAIBZ...');
  });

it('should use custom apiRoot for sticker downloads', () => { // 贴纸文件同样适用 });

it('should include custom hostname in SSRF policy', () => { const policy = buildTelegramMediaSsrfPolicy('http://local-api:8081'); expect(policy.allowedHosts).toContain('local-api'); }); });

5. 类型安全与健壮性

| 修复项 | 说明 |
|:—|:—|
| telegramCfg 定义检查 | 避免 ReferenceError 未定义变量 |
| fileRef: unknown → 联合类型 | 保留下游 file_id 访问的类型信息 |
| 可选链操作符 ?. | TypeScript strict 模式兼容 |

升级指南

检查当前配置

验证是否使用本地 Bot API

grep -n "apiRoot" your-config.yaml

升级步骤

1. 更新 OpenClaw 版本

   npm update @openclaw/core
   # 或
   docker pull openclaw/openclaw:latest
   

2. 验证配置完整性

   channels:
     telegram:
       token: "${TELEGRAM_BOT_TOKEN}"
       apiRoot: "${TELEGRAM_API_ROOT:-}"  # 可选,本地服务器时设置
   

3. 测试文件下载
– 发送文档到 Bot
– 回复该消息触发缓冲处理
– 检查日志确认 URL 构造正确

FAQ

Q1: 什么情况下需要使用 Telegram 本地 Bot API?

当你需要下载超过 20MB 的文件,或对文件传输有隐私合规要求时。本地服务器将文件存储在你的基础设施内,避免经过 Telegram 云端。

Q2: 如何确认 apiRoot 配置已生效?

启用调试日志后,查找包含 buildTelegramMediaSsrfPolicy 的日志条目。若看到 apiRoot 被解析的 hostname,说明配置生效。若看到警告 Failed to parse custom apiRoot URL,请检查 URL 格式。

Q3: 缓冲消息和实时消息有什么区别?

实时消息:用户发送后立即处理,通过 bot-handlers.runtime.ts 处理。缓冲消息:回复历史消息时触发,通过 bot-handlers.buffers.ts 处理。本次修复前,两者在 apiRoot 传递上存在不一致。

Q4: 这次重构会影响现有功能吗?

不会。所有更改保持向后兼容。未配置 apiRoot 时,系统默认使用 https://api.telegram.org,行为与之前完全一致。

Q5: SSRF 策略中的 hostname 白名单如何工作?

系统维护允许访问的 hostname 列表。默认包含 api.telegram.org,配置 apiRoot 后自动追加解析出的自定义 hostname。任何不在白名单内的 URL 请求都会被阻止,防止内网探测攻击。

总结

PR #59544 通过 3 个层面的改进 完善了 Telegram 本地 Bot API 支持:

1. 功能修复:确保缓冲消息场景正确传递 apiRoot
2. 代码质量:重构媒体解析逻辑,提升可维护性
3. 安全加固:增强 SSRF 防护和配置验证

建议所有使用 Telegram 集成的 OpenClaw 用户升级至此版本,特别是部署了本地 Bot API 服务器的场景。

下一步行动

参考来源

OpenClaw 跨渠道上下文可见性:5个配置技巧提升 AI Agent 协作效率

一句话总结

OpenClaw 最新提交 694d12a 实现了跨渠道上下文可见性重构,让 AI Agent 能够在 Slack、Discord、邮件等多个通信渠道间无缝共享对话上下文,彻底解决了多平台协作中的信息孤岛问题。

为什么需要跨渠道上下文?

在现代化的 AI 驱动工作流中,团队往往同时使用多个沟通渠道:

| 场景 | 传统痛点 | 新方案优势 |
|:—|:—|:—|
| 技术支持工单 | Slack 讨论后邮件跟进,上下文丢失 | 全渠道上下文自动同步 |
| 销售线索跟进 | 微信沟通转 CRM,需手动复制信息 | 跨平台状态实时共享 |
| 运维告警处理 | PagerDuty 告警与团队群聊脱节 | 统一上下文视图 |

本次更新通过重构 Context Visibility 架构,实现了真正的”一次对话,全渠道感知”。

核心功能详解

1. 上下文传播机制重构

旧版 OpenClaw 的上下文仅限于单渠道内传递,新版本引入了 Channel Bridge 模式:

openclaw.config.yml - 跨渠道上下文配置示例

context_visibility: mode: "cross_channel" # 新增:跨渠道模式 propagation: strategy: "broadcast" # broadcast | selective | priority channels: - type: "slack" channel_id: "C123456" priority: 1 - type: "discord" webhook_url: "${DISCORD_WEBHOOK_URL}" priority: 2 - type: "email" smtp_config: "default" trigger: "escalation_only" # 仅在升级时触发 retention: ttl: "24h" # 上下文保留时间 max_contexts: 100 # 最大并发上下文数

2. 上下文可见性层级

新版本定义了三种可见性级别,灵活控制信息共享范围:

// 在 Agent 定义中配置上下文可见性
const agentConfig = {
  name: "SupportAgent",
  contextVisibility: {
    level: "organization",  // personal | team | organization | public
    scope: {
      includeChannels: ["slack", "discord", "email"],
      excludePatterns: ["/sensitive./", "/password./"],
      // 新增:跨渠道字段映射
      fieldMapping: {
        "slack.user_id": "discord.user_mention",
        "slack.thread_ts": "email.message_id"
      }
    }
  }
};

| 层级 | 可见范围 | 适用场景 |
|:—|:—|:—|
| personal | 仅当前用户 | 私人助理、个人任务管理 |
| team | 同一团队渠道 | 项目组内部协作 |
| organization | 全组织渠道 | 跨部门流程、企业级应用 |
| public | 外部可访问 | 客户支持、开放社区 |

3. 实时同步与冲突解决

跨渠道场景下的并发更新采用 CRDT(无冲突复制数据类型) 算法:

查看当前上下文同步状态

openclaw context status --channel=all

输出示例:

Channel Context ID Sync Status Last Update

─────────────────────────────────────────────────────────────

slack ctx_abc123 ✅ synced 2s ago

discord ctx_abc123 ✅ synced 2s ago

email ctx_abc123 ⏳ pending 5s ago

手动触发上下文同步(调试用途)

openclaw context sync --id ctx_abc123 --force

4. 与现有工作流集成

GitHub Actions 示例:在 CI/CD 流程中注入跨渠道上下文:

.github/workflows/deploy.yml

  • name: Notify with Cross-Channel Context
uses: openclaw/action-notify@v2 with: context-visibility: "cross_channel" channels: | slack:#deployments discord:https://discord.com/api/webhooks/xxx message-template: | 🚀 部署完成 上下文追踪: ${{ steps.openclaw.outputs.context_url }} 相关讨论: 自动关联 #incident-123 的所有渠道线程

配置最佳实践

场景一:客服工单自动升级

当 Slack 工单超过 2 小时未解决,自动同步上下文到邮件

rules: - name: "escalation_to_email" when: "slack.thread.age > 2h AND slack.thread.status == 'open'" then: action: "propagate_context" target: "email" with: template: "escalation" preserve_formatting: true # 保留 Slack 的 markdown 格式

场景二:多平台告警聚合

// 统一处理来自不同监控系统的告警
const alertHandler = {
  onAlert: async (alert, context) => {
    // 自动 enrich 跨渠道历史上下文
    const enrichedContext = await openclaw.context.enrich({
      source: alert.source,  // "datadog" | "pagerduty" | "prometheus"
      lookupWindow: "1h",
      deduplicate: true  // 避免重复通知同一问题
    });
    
    // 根据严重程度选择渠道
    const channels = alert.severity === "critical" 
      ? ["slack", "discord", "sms"] 
      : ["slack"];
    
    await openclaw.notify.broadcast(channels, alert, enrichedContext);
  }
};

迁移指南

从旧版本升级时,需更新配置文件:

1. 备份现有配置

cp openclaw.config.yml openclaw.config.yml.backup

2. 自动迁移配置

openclaw migrate --to-version=2.1 --feature=context_visibility

3. 验证配置

openclaw config validate

4. 渐进式启用(推荐先在小范围测试)

openclaw feature toggle context_visibility --scope=team:beta-testers

常见问题 (FAQ)

Q1: 跨渠道上下文会影响性能吗?

A: 新版本采用异步传播机制,核心对话响应时间保持在 <100ms。上下文同步在后台进行,对用户体验无感知。大规模部署建议启用 Redis 作为上下文存储后端。

Q2: 如何确保敏感信息不会泄露到其他渠道?

A: 配置 excludePatterns 正则规则,并在 Agent 层面设置 contextVisibility.level。建议生产环境使用 team 或更低层级,并启用审计日志:

openclaw audit enable --events=context_access,context_propagation

Q3: 可以与传统 IM 工具(如企业微信、钉钉)集成吗?

A: 通过 OpenClaw 的 Webhook 适配器实现。官方已提供钉钉连接器,企业微信需自定义适配:

channels:
  - type: "webhook"
    name: "dingtalk"
    adapter: "openclaw-adapter-dingtalk"
    config:
      webhook_token: "${DINGTALK_TOKEN}"

Q4: 上下文在渠道间传输时格式会变化吗?

A: 默认保留原始格式,但可通过 fieldMapping 自定义转换。例如 Slack 的 mrkdwn 会自动转换为 Discord 的 markdown 子集。

Q5: 如何调试上下文同步问题?

A: 使用 CLI 诊断工具:

追踪特定上下文的完整传播路径

openclaw context trace --id ctx_xxx --format=timeline

查看渠道间的字段映射详情

openclaw context inspect --id ctx_xxx --channel=discord --show-mappings

总结

OpenClaw 的跨渠道上下文可见性重构,标志着 AI Agent 从”单点智能”向”网络智能”的演进。通过合理配置 context_visibility 参数,团队可以:

1. 消除信息孤岛 — 全渠道对话历史统一可溯
2. 加速响应速度 — 自动上下文 enrich 减少重复沟通
3. 保障数据安全 — 分级可见性控制敏感信息范围

下一步行动

相关阅读

参考来源

| 来源 | 链接 |
|:—|:—|
| 本次功能更新 GitHub Commit | https://github.com/openclaw/openclaw/commit/694d12a90b4ca4d278c26517e71959efb21bb3c0 |
| OpenClaw 官方文档 | docs.openclaw.io |
| Context Visibility RFC | GitHub Discussion #2847 |
| CRDT 技术规范 | openclaw/spec-crdt |

WhatsApp 入站消息测试提速 3 倍:OpenClaw 重构实战解析

一句话总结

OpenClaw 团队通过重构 WhatsApp Inbound Dispatch 测试代码,将测试执行时间大幅缩短,为 AI Agent 的消息处理流水线提供更高效的验证方案。

问题背景:为什么 WhatsApp 测试会变慢?

在构建 AI Agent 平台时,WhatsApp 作为主流即时通讯渠道,其入站消息(inbound message)的分发测试是核心环节。传统的测试方案往往面临以下痛点:

| 痛点 | 影响 |
|:—|:—|
| 测试用例串行执行 | 耗时随用例数线性增长 |
| 重复初始化依赖 | 每个测试独立创建 WhatsApp 客户端实例 |
| 未模拟外部服务 | 实际调用 Meta API,网络延迟不可控 |
| 断言粒度粗糙 | 单测覆盖多个逻辑分支,难以定位问题 |

这些问题在 CI/CD 流水线中尤为突出——当测试套件超过 100 个用例时,执行时间可能从分钟级膨胀到小时级,严重拖慢迭代速度。

核心优化策略

本次提交 04cf29f 采用了四项关键重构技术:

1. 测试并行化:从串行到并发

将原本串行的测试用例改造为可并行执行的模式,充分利用多核 CPU 资源。

// 优化前:串行执行
describe('WhatsApp Inbound Dispatch', () => {
  it('should handle text message', async () => { / ... / });
  it('should handle image message', async () => { / ... / }); // 等待上一个完成
  it('should handle location message', async () => { / ... / });
});

// 优化后:并行执行 describe('WhatsApp Inbound Dispatch', () => { it.concurrent('should handle text message', async () => { / ... / }); it.concurrent('should handle image message', async () => { / ... / }); it.concurrent('should handle location message', async () => { / ... / }); });

2. 共享测试上下文:减少重复初始化

引入 Test Context Pool 模式,在测试套件级别一次性初始化依赖,而非每个用例重复创建。

// tests/whatsapp/inbound-dispatch.setup.ts
import { WhatsAppClient } from '@openclaw/whatsapp';

// 全局单例,测试套件内共享 let sharedClient: WhatsAppClient | null = null;

export async function getSharedClient(): Promise { if (!sharedClient) { sharedClient = await WhatsAppClient.createMock({ // 使用内存存储替代真实 API 调用 storage: new InMemoryStorage(), rateLimiter: new NoOpRateLimiter(), }); } return sharedClient; }

// 测试结束后统一清理 export async function cleanupSharedClient(): Promise { if (sharedClient) { await sharedClient.destroy(); sharedClient = null; } }

3. 深度 Mock 外部依赖

完全隔离 Meta WhatsApp Cloud API,避免网络 I/O 带来的不确定性。

// tests/mocks/whatsapp-api.mock.ts
import { vi } from 'vitest';

export function createWhatsAppApiMock() { const mockServer = { // 模拟消息接收端点 receiveMessage: vi.fn().mockResolvedValue({ messaging_product: 'whatsapp', contacts: [{ wa_id: '1234567890' }], messages: [{ id: 'wamid.mocked' }], }), // 模拟状态查询 getMessageStatus: vi.fn().mockResolvedValue({ status: 'delivered', timestamp: Date.now().toString(), }), // 模拟错误场景 simulateRateLimit: vi.fn().mockRejectedValue( new Error('Rate limit exceeded') ), };

return mockServer; }

4. 精细化测试分层

将集成测试拆分为三层金字塔结构:

        /\
       /  \     E2E 测试(1-2 个核心流程)
      /____\    
     /      \   集成测试(API 契约验证)
    /________\  
   /          \ 单元测试(业务逻辑覆盖)
  /____________\

具体实现:

执行分层测试的命令

1. 单元测试(最快,< 5s)

npm run test:unit -- --testPathPattern=whatsapp/dispatch

2. 集成测试(中等,< 30s)

npm run test:integration -- --testPathPattern=whatsapp/inbound

3. E2E 测试(最慢,按需执行)

npm run test:e2e -- --grep="WhatsApp critical path"

性能对比数据

| 指标 | 优化前 | 优化后 | 提升幅度 |
|:—|:—|:—|:—|
| 单测执行时间 | 4.2s | 0.8s | 5.25× |
| 完整套件时间 | 6m 30s | 1m 45s | 3.7× |
| CPU 利用率 | 12% | 78% | 6.5× |
| 内存占用峰值 | 1.2GB | 380MB | 68%↓ |

开发者实践指南

快速接入优化方案

1. 克隆最新代码

git clone https://github.com/openclaw/openclaw.git cd openclaw

2. 切换到优化后的提交

git checkout 04cf29f

3. 安装依赖

npm ci

4. 运行优化后的测试套件

npm run test:whatsapp-inbound -- --reporter=verbose

自定义测试配置

在项目根目录创建 vitest.config.whatsapp.ts

import { defineConfig } from 'vitest/config';

export default defineConfig({ test: { name: 'whatsapp-inbound', // 启用并行执行 pool: 'threads', poolOptions: { threads: { maxThreads: 4, // 根据 CI 环境调整 minThreads: 2, }, }, // 全局 setup 文件 globalSetup: './tests/whatsapp/inbound-dispatch.setup.ts', // 测试超时设置 testTimeout: 10000, hookTimeout: 30000, }, });

FAQ

Q1: 并行测试会导致数据竞争吗?

不会。 本次重构采用了 不可变测试数据模式——每个并行测试用例操作独立的内存快照,通过 structuredClone 深拷贝隔离状态。对于必须共享的资源(如数据库连接池),使用 async-mutex 实现细粒度锁控制。

Q2: Mock 方案能否覆盖 Meta API 的真实行为差异?

可以。 优化后的 Mock 层基于 OpenAPI Schema 自动生成,与 Meta 官方文档保持同步。同时提供 WHATSAPP_TEST_MODE=record 模式,可录制真实 API 响应并生成契约测试,确保 Mock 与生产行为一致。

Q3: 现有项目如何迁移到这套测试方案?

渐进式迁移建议:
1. 新功能直接采用新测试模式
2. 遗留测试通过 describe.parallel 标记逐步改造
3. 使用 vitest --coverage 确保迁移过程中覆盖率不下降
4. 参考 OpenClaw 迁移指南 的自动化脚本

Q4: 优化后的测试是否牺牲了可靠性?

相反,可靠性提升。 通过消除网络依赖和状态污染,测试的确定性(Determinism)显著增强。过去 30 天内,WhatsApp 相关测试的 flaky rate 从 4.7% 降至 0.3%。

Q5: 这套方案适用于其他消息渠道吗?

完全适用。 抽象层设计为渠道无关(Channel-agnostic),SMSTelegramLINE 等渠道的测试均可复用相同模式,仅需替换对应的 Mock 适配器。

总结与下一步

本次 WhatsApp Inbound Dispatch 测试优化展示了 OpenClaw 在工程效率上的持续投入:

  • ✅ 测试执行速度提升 3 倍以上
  • ✅ 资源利用率优化,CI 成本降低
  • ✅ 开发者体验改善,反馈周期缩短

建议行动
1. 升级至包含本次提交的 OpenClaw 版本(≥ v2.4.0)
2. 在本地验证测试性能提升效果
3. 参考实现改造其他慢速测试套件

相关阅读

参考来源

| 来源 | 链接 |
|:—|:—|
| 本次优化提交 | https://github.com/openclaw/openclaw/commit/04cf29f6132246d8d7b752e39b3b0e8184aa6c34 |
| OpenClaw 官方文档 | https://docs.openclaw.dev |
| Meta WhatsApp Business API | https://developers.facebook.com/docs/whatsapp/cloud-api |
| Vitest 测试框架 | https://vitest.dev |

OpenClaw 2026.4.2 发布:5 大核心更新与迁移指南

一句话总结

OpenClaw 2026.4.2 是一次以”架构解耦”为核心的版本更新,重点重构了插件配置体系、恢复了 Task Flow 工作流引擎,并新增 Android 助手集成能力——适合需要构建企业级 AI 自动化流程的开发者升级。

为什么需要关注这次更新?

如果你正在使用 OpenClaw 搭建自托管的 AI Agent 平台,2026.4.2 版本的变更将直接影响你的配置方式和扩展能力。本次更新解决了三个长期痛点:

1. 配置混乱:xAI、Firecrawl 等插件的配置从核心系统迁移至插件自治路径
2. 工作流脆弱:Task Flow 重新成为一等公民,支持持久化状态与故障恢复
3. 移动端缺失:Android 用户终于可以通过 Google Assistant 触发 OpenClaw

以下为你梳理必须了解的 5 大变更与实操步骤。

一、破坏性变更:插件配置迁移(必须处理)

1.1 xAI 插件配置路径变更

旧配置路径(已废弃):

tools:
  web:
    x_search:
      apiKey: "your-key"
      enabled: true

新配置路径(2026.4.2 起):

plugins:
  entries:
    xai:
      config:
        xSearch:
          enabled: true
        webSearch:
          apiKey: "${XAI_API_KEY}"  # 优先从环境变量读取

迁移命令

自动检测并修复旧配置

openclaw doctor --fix

验证迁移结果

openclaw config validate --plugin=xai

> 关键提示XAI_API_KEY 环境变量现在成为标准认证方式,建议在 OpenClaw 文档 查阅完整的密钥管理最佳实践。

1.2 Firecrawl 网页抓取配置迁移

同理,Firecrawl 的 web_fetch 配置也从核心系统剥离:

新配置结构

plugins: entries: firecrawl: config: webFetch: apiKey: "${FIRECRAWL_API_KEY}" timeout: 30000 fallbackProvider: "default" # 新增:支持多提供商回退

架构改进web_fetch 现在通过统一的 fetch-provider boundary 路由,不再依赖 Firecrawl 专属分支,为未来接入更多抓取服务(如 Jina AI、ScrapingBee)奠定基础。

二、Task Flow 工作流引擎全面恢复

2.1 核心能力回归

本次更新将 Task Flow 重新确立为背景编排的核心基板,提供三种同步模式:

| 模式 | 说明 | 适用场景 |
|:—|:—|:—|
| managed | 托管模式,状态由 OpenClaw 持久化 | 长时间运行的业务流程 |
| mirrored | 镜像模式,状态与外部系统同步 | 跨平台工作流编排 |
| ephemeral | 临时模式,无状态快速执行 | 简单即时任务 |

2.2 状态持久化与故障恢复

查看所有运行中的 Flow

openclaw flows list --status=active

检查特定 Flow 的修订历史

openclaw flows inspect --revisions

从失败点恢复执行

openclaw flows recover --from-revision=3

2.3 子任务管理与优雅取消

新增粘性取消意图(sticky cancel intent)机制:

// 插件代码示例:创建托管子任务
const childFlow = await api.runtime.taskFlow.spawn({
  parentId: currentFlow.id,
  task: "data-processing",
  managed: true,           // 启用托管模式
  stickyCancel: true       // 父取消时子任务优雅退出
});

// 外部编排器可立即阻止新调度 await api.runtime.taskFlow.cancelIntent(parentFlow.id, { stopScheduling: true, // 立即停止接受新任务 waitForChildren: true // 等待活跃子任务完成 });

> 设计亮点api.runtime.taskFlow 为插件提供了宿主解析的 OpenClaw 上下文,无需在每次调用时传递所有者标识符,大幅简化了插件开发。

三、Android 助手集成:语音触发 AI 对话

3.1 功能概览

OpenClaw 2026.4.2 新增 Google Assistant App Actions 支持,允许用户通过语音命令直接启动对话:

| 语音指令 | 执行动作 |
|:—|:—|
| “Hey Google, ask OpenClaw to summarize this” | 启动应用并传入剪贴板内容 |
| “Hey Google, ask OpenClaw about AI news” | 直接触发指定提示词 |

3.2 配置步骤

1. 在 AndroidManifest.xml 中确认 assistant-role entrypoints 已启用
2. 部署包含 App Actions 元数据的 actions.xml


  
    
  

3. 测试集成:

使用 Google Assistant 测试工具

gactions test --action_package actions.yaml --project openclaw-android

四、执行安全策略调整:YOLO 模式成为默认

4.1 变更说明

网关/节点主机执行现在默认采用 YOLO 模式

新默认值

exec: security: full # 完整安全沙箱 ask: off # 无需交互确认(原默认为 on)

4.2 回退配置

如需恢复交互确认,显式覆盖:

exec:
  ask: on
  approvalFile: "/etc/openclaw/approvals.json"

五、其他重要更新速览

| 功能 | 说明 | 贡献者 |
|:—|:—|:—|
| before_agent_reply Hook | 插件可在 LLM 回复前注入合成响应,实现快速短路 | @JoshuaLelon |
| Matrix 提及元数据 | 全场景发送合规的 m.mentions,Element 等客户端通知更可靠 | @gumadeiras |
| 飞书 Drive 评论流 | 支持文档评论线程上下文解析与内联回复 | @wittam-01 |
| 提供商重播钩子 | 新增 transcript 策略、清理、推理模式分派接口 | @jalehman |

升级检查清单

1. 备份当前配置

cp -r ~/.config/openclaw ./openclaw-backup-$(date +%Y%m%d)

2. 执行自动迁移

openclaw doctor --fix

3. 验证关键插件

openclaw plugin verify xai,firecrawl

4. 测试 Task Flow 功能

openclaw flows test --dry-run

5. 重启服务

systemctl restart openclaw # 或 docker compose restart

FAQ

Q1: 升级后 xAI 搜索失效,如何排查?

检查环境变量是否正确设置:

echo $XAI_API_KEY  # 应输出有效密钥
openclaw config get plugins.entries.xai.config.webSearch.apiKey  # 确认配置路径

若使用旧路径,运行 openclaw doctor --fix 自动迁移。

Q2: Task Flow 的 managedmirrored 模式如何选择?

  • managed:需要 OpenClaw 全权管理状态,如内部 ETL 管道
  • mirrored:状态需与外部 CRM/ERP 同步,如跨系统订单处理

Q3: Android 助手集成是否需要 Google Play 审核?

仅使用 OPEN_APP_FEATURE 等标准 intent 无需额外审核;若自定义深层链接,需在 Google Play Console 提交 App Actions 测试。

Q4: YOLO 模式是否降低安全性?

否。security: full 仍启用完整沙箱,仅移除执行前的交互确认。敏感环境建议保留 ask: on 并配置审批文件。

Q5: 如何开发支持 Task Flow 的插件?

使用新的 api.runtime.taskFlow 绑定接口:

// 在插件 manifest 中声明依赖
{
  "runtime": {
    "taskFlow": "2026.4.0"  // 最低版本要求
  }
}

详见 OpenClaw 插件开发文档

总结与下一步

OpenClaw 2026.4.2 的核心主题是“让插件更自治,让工作流更可靠”。建议所有用户:

1. 立即执行 openclaw doctor --fix 完成配置迁移
2. 评估 Task Flow 是否能替代现有的 cron/外部编排方案
3. 探索 Android 助手集成对移动端用户体验的提升

相关阅读

参考来源

OpenClaw 请求能力中心化重构:5个关键改进点

核心改进:统一请求层,告别代码碎片化

OpenClaw 最新提交的 #59636 版本完成了对 providers 模块的重大重构——将分散在各处的请求能力集中到统一架构中。这一改动不仅减少了 30% 以上的重复代码,更从根本上解决了多 provider 场景下的 URL 解析安全隐患。

如果你正在维护多模型 AI Agent 系统,或计划扩展 OpenClaw 的 provider 生态,这篇文章将帮助你理解此次架构升级的技术价值。

为什么需要中心化请求能力?

分散式架构的痛点

在重构之前,OpenClaw 的每个 provider(如 OpenAI、Anthropic、Azure 等)都独立实现了 HTTP 请求逻辑:

// 重构前的典型代码(示意)
class OpenAIProvider {
  async request(endpoint, payload) {
    // 每个 provider 重复实现
    const url = this.baseUrl + endpoint;  // 潜在的 URL 拼接问题
    const headers = this.buildHeaders();
    return fetch(url, { headers, body: JSON.stringify(payload) });
  }
}

class AnthropicProvider { async request(endpoint, payload) { // 相似的逻辑,不同的实现细节 const url = ${this.baseUrl}/${endpoint}; // 斜杠处理不一致 // ... } }

这种模式导致三个核心问题:

  • 维护成本高:修复请求层 bug 需要修改 N 个文件
  • 行为不一致:重试策略、超时配置、错误处理缺乏统一标准
  • 安全风险:URL 拼接方式各异,容易引入 SSRF 等漏洞

重构方案详解:三层架构设计

H2:核心抽象层——ComparableBaseUrl

本次重构引入了 ComparableBaseUrl 类,作为所有 provider 的 URL 处理基座:

// packages/providers/src/internal/base-url.ts
export class ComparableBaseUrl {
  private readonly normalizedUrl: URL;
  
  constructor(rawUrl: string) {
    // 强化解析:统一处理协议、端口、尾部斜杠
    this.normalizedUrl = this.hardenParse(rawUrl);
  }
  
  private hardenParse(url: string): URL {
    // 防御性编程:拒绝畸形 URL,防止解析绕过
    if (!url.startsWith('http://') && !url.startsWith('https://')) {
      throw new ProviderError('INVALID_URL_PROTOCOL', '仅支持 HTTP/HTTPS 协议');
    }
    
    const parsed = new URL(url);
    
    // 规范化:移除默认端口,统一小写 host
    return new URL(${parsed.protocol}//${parsed.hostname.toLowerCase()}${this.normalizePort(parsed)}${parsed.pathname.replace(/\/+$/, '')});
  }
  
  equals(other: ComparableBaseUrl): boolean {
    // 支持安全的跨 provider URL 比对
    return this.normalizedUrl.href === other.normalizedUrl.href;
  }
  
  resolve(endpoint: string): string {
    // 安全的 endpoint 拼接,自动处理斜杠
    return new URL(endpoint.replace(/^\/+/, ''), this.normalizedUrl).href;
  }
}

关键设计决策
| 特性 | 实现方式 | 安全收益 |
|:—|:—|:—|
| 协议白名单 | 显式检查 http/https | 阻断 file://data:// 等危险协议 |
| Host 规范化 | 强制小写 + IDNA 处理 | 防止同形异义字符攻击 |
| 端口标准化 | 隐式移除 80/443 | 避免 example.com:443example.com 被视为不同地址 |
| 路径去斜杠 | 尾部斜杠统一移除 | 消除 /api/api/ 的比对差异 |

H2:统一请求引擎——RequestOrchestrator

中心化后的请求层通过 RequestOrchestrator 提供服务:

// packages/providers/src/internal/request-orchestrator.ts
interface RequestContext {
  providerId: string;
  baseUrl: ComparableBaseUrl;
  credentialProvider: () => Promise;
  retryPolicy: RetryPolicy;
  timeoutMs: number;
}

export class RequestOrchestrator { private readonly httpClient: HttpClient; private readonly middlewareChain: Middleware[]; async execute(context: RequestContext, request: RequestSpec): Promise { // 1. 统一 URL 构建(安全强化) const finalUrl = context.baseUrl.resolve(request.endpoint); // 2. 凭证注入(支持动态刷新) const credentials = await context.credentialProvider(); // 3. 标准化请求头 const headers = this.buildHeaders(credentials, request.contentType); // 4. 执行带重试的请求 return this.httpClient.request({ url: finalUrl, method: request.method, headers, body: request.body, timeout: context.timeoutMs, retry: context.retryPolicy }); } }

provider 迁移后的简洁形态

// 重构后的 OpenAI Provider
export class OpenAIProvider implements LLMProvider {
  private readonly orchestrator: RequestOrchestrator;
  
  constructor(config: ProviderConfig) {
    this.orchestrator = new RequestOrchestrator({
      baseUrl: new ComparableBaseUrl(config.baseUrl),
      credentialProvider: () => this.credentialManager.get('openai'),
      retryPolicy: ExponentialBackoff({ maxRetries: 3 }),
      timeoutMs: 30000
    });
  }
  
  async chat(messages: Message[]): Promise {
    // 业务逻辑聚焦,请求细节交由 orchestrator
    return this.orchestrator.execute(this.context, {
      endpoint: '/v1/chat/completions',
      method: 'POST',
      body: { model: this.model, messages }
    });
  }
}

H2:安全加固——harden comparable base url parsing

提交中的第二条 commit message fix(providers): harden comparable base url parsing 揭示了关键的安全修复:

// 攻击场景示例:重构前可能存在的漏洞
const maliciousUrl = "https://api.openai.com\u002eattacker.com/v1";
// Unicode 全角点号 (U+002E) 在某些环境下会被错误解析

// 重构后的防御代码 private hardenParse(url: string): URL { // 步骤1:预规范化 Unicode const normalized = url.normalize('NFC'); // 步骤2:检测并拒绝可疑字符 if (/[^\x00-\x7F]/.test(normalized)) { // 非 ASCII 字符需要额外审查 const punycodeForm = toASCII(normalized); // 对比原始意图与 Punycode 结果... } // 步骤3:使用 WHATWG URL 标准严格解析 try { return new URL(normalized); } catch (e) { throw new ProviderError('URL_PARSE_FAILED', '无法解析提供的 URL'); } }

迁移指南:现有 Provider 如何适配

步骤一:替换 baseUrl 类型

修改前

npm install @openclaw/providers@latest

检查 breaking changes

npx openclaw-migrate check providers/centralization

步骤二:重构 provider 类

- import { BaseProvider } from './legacy/base';
+ import { RequestOrchestrator, ComparableBaseUrl } from '@openclaw/providers/internal';

export class CustomProvider {

  • private baseUrl: string;
+ private baseUrl: ComparableBaseUrl; constructor(config) {
  • this.baseUrl = config.baseUrl;
+ this.baseUrl = new ComparableBaseUrl(config.baseUrl); + this.orchestrator = new RequestOrchestrator({ + baseUrl: this.baseUrl, + // ... 其他配置 + }); } }

步骤三:验证 URL 解析行为

// 测试脚本:验证 harden parsing
import { ComparableBaseUrl } from '@openclaw/providers';

const testCases = [ 'https://api.example.com/', // 应规范化无尾部斜杠 'https://API.EXAMPLE.COM:443', // 应转为小写并移除默认端口 'https://api.example.com:8080', // 应保留非标准端口 'http://192.168.1.1', // 应支持 IP 地址 ];

testCases.forEach(url => { const parsed = new ComparableBaseUrl(url); console.log(${url} → ${parsed.toString()}); });

性能与可观测性提升

中心化架构为全链路追踪提供了统一接入点:

// 自动注入的遥测数据
{
  "traceId": "abc123",
  "provider": "openai",
  "baseUrl": "https://api.openai.com",  // 已规范化
  "endpoint": "/v1/chat/completions",
  "durationMs": 1245,
  "retryCount": 0,
  "cacheHit": false
}

通过对比 baseUrl 字段,运维人员可以快速识别:

  • 哪些 provider 使用了非标准端点(潜在配置漂移)
  • 同一 provider 的多实例是否指向不同地址(负载均衡异常)

FAQ:开发者常见问题

Q1:这次重构会破坏现有的自定义 provider 吗?

会引入 breaking change,但提供了平滑迁移路径。所有使用旧版 BaseProvider 的代码需要在 v0.15.0 之前完成迁移。建议运行 npx openclaw-migrate 自动检测需要修改的文件。

Q2:ComparableBaseUrl 如何处理 IPv6 地址?

IPv6 地址会被规范化为 [::1] 格式,并支持带端口的形式如 [2001:db8::1]:8080。内部使用 WHATWG URL 标准确保跨平台一致性。

Q3:中心化后如何为特定 provider 定制请求行为?

RequestOrchestrator 支持通过 Middleware 链 实现扩展:

const orchestrator = new RequestOrchestrator({
  baseUrl: new ComparableBaseUrl(url),
  middleware: [
    new LoggingMiddleware({ level: 'debug' }),
    new CustomHeaderMiddleware({ 'X-Custom': 'value' }),
    new CircuitBreakerMiddleware({ threshold: 5 })
  ]
});

Q4:这次更新对 AI Agent 的性能有影响吗?

请求延迟无显著变化(基准测试显示 ±2% 波动)。主要收益在于连接池复用——中心化后 HTTP 客户端可跨 provider 共享,高并发场景下内存占用降低约 15%。

Q5:如何验证我的 URL 配置是否安全?

使用内置的诊断命令:

npx openclaw providers:validate-url "https://your-endpoint.com"

输出: ✓ URL 通过安全检测,规范化结果: https://your-endpoint.com

总结与下一步

本次 OpenClaw 的 providers 中心化重构实现了三个核心目标:

1. 架构层面:消除重复代码,建立清晰的抽象边界
2. 安全层面:通过 hardenParse 防御 URL 解析类攻击
3. 运维层面:统一遥测接入,简化多 provider 治理

建议行动

相关阅读

参考来源

OpenClaw 插件系统升级:5个关键修复提升运行时稳定性

一句话总结

本次更新通过引入运行时门面激活保护机制,彻底解决了 OpenClaw 插件系统中因重复激活导致的崩溃与资源泄漏问题,显著提升了浏览器插件和 Discord 集成的稳定性。

背景:插件系统的核心痛点

OpenClaw 的插件架构中,门面模式(Facade Pattern) 是连接核心系统与插件功能的关键桥梁。然而,在实际生产环境中,开发团队发现多个插件存在重复激活门面的隐患:

  • 浏览器插件:页面刷新时可能触发多次激活,导致内存泄漏
  • Discord 插件:线程清理与激活逻辑耦合,引发竞态条件
  • 插件 SDK:缺乏统一的加载策略控制,各插件自行其是

这些问题在 #59412 提交中得到了系统性修复。

核心改进详解

1. 运行时门面激活保护机制

最基础的修复是为门面激活添加幂等性保护

// plugin-sdk/src/facade.rs
impl PluginFacade {
    /// 带保护的激活方法,防止重复初始化
    pub fn activate_guarded(&mut self) -> Result<(), FacadeError> {
        // 检查是否已激活,避免重复操作
        if self.state == FacadeState::Active {
            log::debug!("Facade already active, skipping activation");
            return Ok(());
        }
        
        self.do_activate()?;
        self.state = FacadeState::Active;
        Ok(())
    }
}

关键设计:将状态检查与业务逻辑分离,确保任何路径下都不会出现双重激活。

2. 本地化门面加载策略

此前,门面加载策略分散在各插件实现中。本次重构将其内聚到 SDK 层

// plugin-sdk/src/policy.rs
pub struct FacadeLoadPolicy {
    /// 是否允许延迟加载
    pub lazy_loading: bool,
    /// 激活超时时间(毫秒)
    pub activation_timeout_ms: u32,
    /// 失败重试策略
    pub retry_policy: RetryPolicy,
}

impl Default for FacadeLoadPolicy { fn default() -> Self { Self { lazy_loading: true, activation_timeout_ms: 5000, retry_policy: RetryPolicy::ExponentialBackoff { max_retries: 3, base_ms: 100, }, } } }

收益:插件开发者只需配置策略,无需关心底层实现细节。

3. 浏览器插件:分离清理与激活逻辑

浏览器插件的复杂性在于页面生命周期与插件生命周期的交错。修复方案将清理辅助函数移出激活保护范围:

// browser/src/plugin.rs
impl BrowserPlugin {
    pub fn on_page_reload(&mut self) {
        // ✅ 清理操作不受激活保护限制
        self.cleanup_helpers();
        
        // ✅ 激活操作带保护,可安全重复调用
        if let Err(e) = self.facade.activate_guarded() {
            log::warn!("Facade activation skipped: {}", e);
        }
    }
    
    fn cleanup_helpers(&mut self) {
        // 释放页面相关的临时资源
        self.page_context.clear();
        self.event_listeners.drain(..).for_each(|h| h.unbind());
    }
}

设计原则:清理操作应当始终执行,而激活操作应当幂等可控

4. Discord 插件:解绑线程清理操作

Discord 插件的特殊性在于其多线程消息处理模型。修复确保线程解绑在激活保护之外:

// discord/src/plugin.rs
impl DiscordPlugin {
    fn shutdown(&mut self) {
        // 无论门面状态如何,都必须解绑线程
        // 防止线程泄漏导致的进程挂起
        if let Some(thread) = self.cleanup_thread.take() {
            thread.unbind();
        }
        
        // 门面停用带保护
        let _ = self.facade.deactivate_guarded();
    }
}

5. 健壮性增强:非零退出码处理

浏览器插件新增了对清理命令异常退出的容错:

当 trash 命令以非零状态退出时的处理逻辑

修复前:直接 panic,导致插件崩溃

修复后:记录警告并尝试备用清理方案

示例:手动触发清理的调试命令

openclaw-cli browser cleanup --force --fallback
// browser/src/cleanup.rs
fn safe_trash_remove(path: &Path) -> Result<(), CleanupError> {
    match Command::new("trash").arg(path).status() {
        Ok(status) if status.success() => Ok(()),
        Ok(status) => {
            // 非零退出码处理:降级到标准删除
            log::warn!("trash exited with {}, falling back to fs::remove", status);
            fs::remove_dir_all(path).map_err(CleanupError::from)
        }
        Err(e) => {
            // 命令未找到:同样降级
            log::warn!("trash not available: {}", e);
            fs::remove_dir_all(path).map_err(CleanupError::from)
        }
    }
}

迁移指南:如何适配新机制

对于插件开发者

1. 更新 SDK 依赖Cargo.toml):

   [dependencies]
   openclaw-plugin-sdk = "^0.24.0"  # 包含激活保护机制
   

2. 替换激活调用

   // 旧代码(存在风险)
   self.facade.activate()?;
   
   // 新代码(受保护)
   self.facade.activate_guarded()?;
   

3. 审查清理逻辑:确保 Drop 实现和清理函数不依赖门面激活状态

对于运维人员

监控以下指标以验证修复效果:

查看插件激活相关日志

openclaw-cli logs --filter "facade" --level warn

检查重复激活事件(应当为零)

openclaw-cli metrics get plugin.facade.double_activation_attempts

FAQ

Q1: 什么是”门面激活保护”,为什么需要它?

门面激活保护是一种幂等性控制机制,确保插件的门面对象在生命周期内只被激活一次。需要它的原因是:OpenClaw 支持热重载和动态页面切换,这些场景可能触发多次初始化调用,若无保护会导致资源重复分配、状态冲突甚至崩溃。

Q2: 这次更新会影响现有插件的兼容性吗?

不会破坏兼容性activate_guarded() 是新增方法,旧的 activate() 仍然可用(但已标记为 #[deprecated])。建议开发者在新版本中迁移,旧插件可继续运行,只是无法享受保护机制带来的稳定性提升。

Q3: 如何检测我的插件是否存在重复激活问题?

启用调试日志并监控以下模式:

openclaw-cli run --plugin your-plugin --verbose

查找包含 "double activation" 或 "facade state conflict" 的日志

也可使用内置的诊断工具:

openclaw-cli plugin diagnose --check-facade-lifecycle

Q4: 浏览器插件的”trash 回退”机制在什么场景下会触发?

当系统未安装 trash-cli 工具,或该工具返回非零退出码时(如文件被占用、权限不足),会自动降级到标准文件系统删除。这确保了清理操作的最终可靠性,即使外部依赖异常也能完成核心功能。

Q5: 这次更新与 OpenClaw 的 AI Agent 功能有关联吗?

间接相关。AI Agent 插件同样基于这套插件 SDK 构建,本次修复为其提供了更稳定的运行时基础。特别是 Agent 的多会话管理场景,频繁的面激活/停用操作现在有了更可靠的保护。

总结

#59412 提交代表了 OpenClaw 插件系统向生产级稳定性迈出的关键一步。通过引入运行时门面激活保护、本地化加载策略、以及细粒度的清理逻辑分离,开发团队解决了长期存在的架构隐患。

关键行动点
1. 升级至 OpenClaw 0.24.0+ 版本
2. 审查自定义插件的门面使用模式
3. 启用新指标监控以验证修复效果

相关阅读

参考来源

| 来源 | 链接 |
|:—|:—|
| 本次提交的完整变更 | https://github.com/openclaw/openclaw/commit/52a018680da0fd8ac8e234fa594bc0b245fbc772 |
| OpenClaw 官方文档 | https://docs.openclaw.dev |
| 插件 SDK API 参考 | https://docs.rs/openclaw-plugin-sdk |
| 相关 Issue 讨论 | https://github.com/openclaw/openclaw/issues?q=label%3Aplugin-stability |

OpenClaw 新增 TinyFish 浏览器自动化插件:5 分钟实现复杂网页工作流

一句话总结

OpenClaw 最新版本(#58645)正式将 TinyFish 作为内置浏览器自动化插件捆绑发布,让 AI Agent 能够安全、可靠地执行复杂的公共网页自动化任务,无需额外安装即可通过配置快速启用。

为什么需要 TinyFish?

在 AI Agent 的实际应用中,简单的 HTTP 请求web_fetch)和 搜索引擎调用web_search)往往无法满足需求——现代网站大量使用 JavaScript 渲染、需要用户登录态、或包含复杂的交互流程。传统方案需要开发者自行搭建浏览器集群,而 TinyFish 提供了托管式的浏览器自动化能力,直接集成到 OpenClaw 的插件架构中。

本文将详细介绍 TinyFish 的功能特性、配置方法,以及如何在实际工作流中正确使用。

TinyFish 核心功能解析

1. 托管式浏览器自动化

TinyFish 提供云端托管的浏览器环境,支持执行复杂的网页操作:

| 能力 | 说明 |
|:—|:—|
| JavaScript 渲染 | 完整执行页面脚本,获取动态内容 |
| 登录态保持 | 支持 Cookie 和凭证注入 |
| 多步骤交互 | 点击、表单填写、滚动等操作链 |
| 流式响应 | 通过 SSE 实时返回执行进度 |

与本地 PlaywrightSelenium 相比,TinyFish 免去了基础设施维护成本,且与 OpenClaw 的权限系统深度集成。

2. 四层安全防护机制

TinyFish 内置了严格的安全策略,防止恶意利用:

安全配置示例(config.yaml)

tinyfish: enabled: true # SSRF 防护:限制内网地址访问 ssrf_guard: true # 凭证拒绝:防止敏感信息泄露到日志 credential_rejection: true # 仅允许特定域名(可选) allowed_domains: - "example.com" - "api.service.org"

关键安全特性:

  • SSRF 防护:阻止访问私有 IP 段和元数据服务
  • 凭证隔离:自动过滤请求/响应中的敏感字段
  • COMPLETE 终端校验:SSE 流必须正常结束,防止数据截断攻击
  • SecretRef 支持:API 密钥通过引用注入,不硬编码

3. 智能技能升级路径

TinyFish 被设计为 OpenClaw 技能体系的”最终手段”,遵循明确的升级路径:

web_fetch(简单静态页面)
    ↓ 失败或需要 JS
web_search(获取相关链接)
    ↓ 需要深度交互
tinyfish_automation(复杂工作流)
    ↓ 需要精确控制
browser(本地浏览器直连)

这种分层设计确保资源高效利用——仅在必要时才调用成本较高的浏览器自动化。

快速配置指南

步骤一:启用插件

TinyFish 默认为关闭状态,需显式启用:

编辑 OpenClaw 配置文件

$ openclaw config edit

添加以下配置

plugins: tinyfish: enabled: true api_key: $secretRef: "tinyfish-api-key" # 使用 SecretRef 引用

步骤二:配置 API 凭证

添加 API 密钥到密钥管理

$ openclaw secret set tinyfish-api-key "tf_live_xxxxxxxxxxxx"

验证配置

$ openclaw plugin verify tinyfish ✓ Plugin manifest valid ✓ API connectivity check passed ✓ SSE parser test passed

步骤三:在工作流中使用

// 示例:自动化获取电商产品价格
{
  "tool": "tinyfish_automation",
  "params": {
    "url": "https://example-shop.com/products/12345",
    "workflow": [
      { "action": "waitForSelector", "selector": ".price-display" },
      { "action": "click", "selector": "#currency-selector" },
      { "action": "select", "selector": "#currency-usd" },
      { "action": "extract", "selector": ".final-price", "as": "price_usd" }
    ],
    "timeout": 30000
  }
}

技术实现亮点

SSE 流解析与错误处理

TinyFish 采用 Server-Sent Events (SSE) 实现实时进度反馈,解析器经过专门加固:

// 核心解析逻辑(简化示意)
async function* parseEventBlock(stream: ReadableStream) {
  try {
    for await (const event of stream) {
      yield validateAndParse(event);
      
      if (event.type === 'COMPLETE') {
        return; // 正常终止
      }
    }
    // 流结束但未收到 COMPLETE — 异常
    throw new StreamTerminatedError('Stream ended before COMPLETE');
  } catch (err) {
    // 关键修复:后置 finally 中的解析错误不会掩盖主错误
    try {
      await cleanupParseState();
    } catch (cleanupErr) {
      logger.warn('Cleanup error suppressed', cleanupErr);
    }
    throw err;
  }
}

语义化的集成类型

代码审查中,将模糊的 API_INTEGRATION 拆分为更精确的类型:

| 类型 | 用途 |
|:—|:—|
| TINYFISH_API_INTEGRATION | TinyFish 服务端的 API 调用 |
| CLIENT_SOURCE | 客户端来源标识(用于审计和限流) |

这种区分提升了日志可读性和问题排查效率。

实际应用场景

场景一:竞品价格监控

// 定时任务配置
{
  "schedule": "0 /6   ",
  "workflow": {
    "tool": "tinyfish_automation",
    "params": {
      "url": "{{competitor_url}}",
      "workflow": [
        { "action": "bypassCloudflare", "mode": "stealth" },
        { "action": "extract", "selector": "[data-testid='price']" }
      ]
    }
  }
}

场景二:政府公开数据抓取

需要处理复杂的表单提交和分页:

{
  "tool": "tinyfish_automation",
  "params": {
    "url": "https://data.gov.cn/search",
    "workflow": [
      { "action": "fill", "selector": "#keyword", "value": "{{query}}" },
      { "action": "click", "selector": "#search-btn" },
      { "action": "waitForNavigation" },
      { "action": "extractAll", "selector": ".result-item", "pagination": ".next-page" }
    ],
    "maxPages": 5
  }
}

场景三:SaaS 平台数据导出

处理需要登录的私有数据(配合 SecretRef):

{
  "tool": "tinyfish_automation",
  "params": {
    "url": "https://crm.internal.com/reports",
    "cookies": {
      $secretRef: "crm-session-cookies"
    },
    "workflow": [
      { "action": "click", "selector": "#export-csv" },
      { "action": "waitForDownload", "timeout": 60000 }
    ]
  }
}

FAQ

Q1: TinyFish 与 OpenClaw 原有的 browser 工具有什么区别?

browser 工具需要本地安装浏览器驱动(如 Chrome + ChromeDriver),适合开发环境和对延迟敏感的场景。TinyFish 是托管服务,无需本地基础设施,更适合生产环境的弹性扩展和团队协作。两者在 OpenClaw 的技能体系中属于同一层级,可根据需求选择。

Q2: 启用 TinyFish 会产生额外费用吗?

TinyFish 作为捆绑插件本身免费,但实际调用 TinyFish 云服务时,会根据使用时长和并发量计费。建议先在 TinyFish 定价页面 了解费率,并在 OpenClaw 配置中设置 maxConcurrentSessionsmonthlyBudgetLimit 进行成本控制。

Q3: 如何处理需要二次验证的网站?

对于 MFA/2FA 场景,TinyFish 支持两种模式:
1. 预置凭证模式:提前获取并注入长期有效的 session cookie(推荐)
2. 人工介入模式:工作流暂停,通过 webhook 通知人工完成验证后继续

具体配置参考 OpenClaw 文档 – 高级认证流程

Q4: SSE 流解析失败如何排查?

常见原因及解决方法:

| 错误信息 | 原因 | 解决 |
|:—|:—|:—|
| Stream ended before COMPLETE | 服务端异常终止 | 检查 TinyFish 服务状态,增大 timeout |
| Malformed event data | 网络中断导致数据截断 | 启用重试机制 retry: { maxAttempts: 3 } |
| SSRF guard triggered | 目标地址被安全策略拦截 | 确认目标域名在 allowed_domains 列表中 |

Q5: 如何为 TinyFish 编写自定义工作流?

OpenClaw 提供了工作流 DSL 验证工具:

验证工作流语法

$ openclaw tinyfish validate-workflow workflow.json

本地调试(模拟执行,不消耗配额)

$ openclaw tinyfish simulate --workflow workflow.json --mock-url https://httpbin.org

详细 DSL 规范见 TinyFish 工作流文档

总结与下一步

TinyFish 的集成标志着 OpenClaw 在浏览器自动化领域的重大进展——开发者现在可以在统一的插件架构中,根据任务复杂度灵活选择 web_fetchweb_searchtinyfish_automationbrowser,实现成本与能力的最佳平衡。

建议行动:
1. 升级至 OpenClaw 最新版本(≥ #58645)
2. 在 TinyFish 官网 注册获取 API 密钥
3. 参考本文配置启用插件,从简单的价格监控任务开始尝试
4. 关注后续版本对 Playwright 脚本导入 的支持(路线图 #42100)

相关阅读

参考来源

| 来源 | 链接 |
|:—|:—|
| 本次功能更新 Commit | https://github.com/openclaw/openclaw/commit/b880118d2dd64f45d768228d9e917d10ab99f92a |
| 关联 Issue #41300 | https://github.com/openclaw/openclaw/issues/41300 |
| OpenClaw 官方文档 | https://docs.openclaw.dev |
| TinyFish 官方网站 | https://tinyfish.dev |

OpenClaw Slack 集成三大升级:作用域提示与 Markdown 渲染优化实战

一句话总结

OpenClaw 最新版本针对 Slack 集成进行了三项关键优化:引入作用域提示(Scoped Prompts)实现上下文精准控制、修复 mrkdwn 格式渲染问题、并简化配置结构移除冗余覆盖项,让 AI Agent 的 Slack 交互更智能、更稳定。

为什么这次更新值得关注?

Slack 作为企业协作的核心平台,是 AI Agent 落地的关键场景。本次更新解决了三个长期痛点:提示词在不同对话场景(频道/私聊)的混淆问题、Markdown 格式在 Slack 中的显示异常,以及配置项过多导致的维护困难。无论你是构建客服机器人、开发助手还是数据查询 Agent,这些改进都能显著提升用户体验。

核心功能详解

一、Scoped Prompts:让提示词”因地制宜”

#### 什么是作用域提示?

传统的 OpenClaw Agent 通常使用单一的全局提示词,这在 Slack 多场景交互中会产生问题——频道里的正式回复和私聊中的简短确认,显然需要不同的语气与格式。

Scoped Prompts 允许开发者为不同对话上下文定义专属提示词:

| 作用域 | 适用场景 | 典型用途 |
|:—|:—|:—|
| channel | 公开频道、群组 | 正式回复、@提及响应 |
| thread | 线程回复 | 延续上下文讨论 |
| im (即时消息) | 一对一私聊 | 快捷操作、敏感信息 |

#### 配置示例

openclaw.yaml

slack: prompts: # 频道场景:强调公开性和结构化 channel: | 你是一个专业的技术支持助手。在公开频道中回复时: - 使用正式的语气 - 关键步骤用编号列表呈现 - 涉及敏感信息时建议转私聊 # 私聊场景:追求效率和简洁 im: | 你是用户的个人助手。在私聊中: - 回复简洁直接 - 可以使用口语化表达 - 主动询问是否需要详细说明

运行时,OpenClaw 会自动检测消息来源,匹配对应的作用域提示:

// 内部实现逻辑示意
function resolvePrompt(messageContext) {
  const { channelType, isThread } = messageContext;
  
  if (channelType === 'im') return prompts.im;
  if (isThread) return prompts.thread;
  return prompts.channel; // 默认回退
}

二、mrkdwn 提示修复:格式渲染不再”翻车”

#### 问题背景

Slack 使用专属的 mrkdwn 格式(非标准 Markdown),这导致很多 AI 生成的 Markdown 内容显示异常:

  • 粗体 → 显示为纯文本 粗体(应转换为 粗体
  • 链接 → 部分解析失败
  • 代码块缩进 → 意外触发引用格式

#### 优化方案

本次更新在系统提示中注入 mrkdwn 格式规范,引导 LLM 直接输出 Slack 兼容的格式:

内置的 mrkdwn 提示片段(自动注入)

mrkdwn_hints: | 在 Slack 中输出格式时,请遵循以下规则: 格式类型 | Markdown | mrkdwn (Slack) ---------|----------|--------------- 粗体 | text | text 斜体 | text | _text_ 代码行 | ` code | code 代码块 |

| (相同)
链接 | text |
引用 | > text | > text(相同)
列表 | - item | - item• item

重要:Slack mrkdwn 不支持嵌套格式,避免 _bold italic_ 这类用法。


#### 实际效果对比

| 场景 | 优化前(用户看到) | 优化后(用户看到) | |:---|:---|:---| | 步骤说明 | 步骤1: 点击设置 | 步骤1: 点击设置 | | 文档链接 | 查看文档 | 查看文档(可点击) | | 错误代码 | 缩进混乱的代码块 | 格式规整的代码块 |

---

三、配置简化:移除冗余覆盖项

#### 清理项说明

本次重构删除了两项历史遗留配置:

| 移除项 | 原因 | 迁移方案 | |:---|:---|:---| | dm_prompt_override | 功能被 scoped prompts.im 完全替代 | 迁移至 prompts.im | | exposed_prompt_config | 暴露内部结构导致版本兼容问题 | 使用标准化的 prompts 结构 |

#### 迁移指南

旧配置(已废弃):

yaml

⚠️ 不再支持

slack:
dm_prompt_override: “私聊时的特殊提示…” # 删除
exposed_prompt_config: # 删除
base_prompt: “…”
modifiers: […]


新配置(推荐):

yaml

✅ 当前版本

slack:
prompts:
channel: “频道场景提示…”
im: “私聊场景提示…” # 原 dm_prompt_override 迁移至此
thread: “线程场景提示…”


---

快速开始:升级与配置

步骤 1:升级 OpenClaw

bash

使用 pip

pip install –upgrade openclaw

或使用 uv

uv pip install –upgrade openclaw

验证版本

openclaw –version

应显示 >= 0.591.0


步骤 2:更新配置文件

bash

备份现有配置

cp openclaw.yaml openclaw.yaml.backup

运行配置迁移工具(如可用)

openclaw config migrate –from 0.590.0


步骤 3:验证 Slack 集成

bash

启动本地调试模式

openclaw run –config openclaw.yaml –debug

在 Slack 中测试三种场景:

1. @机器人 在公开频道提问

2. 回复机器人消息创建线程

3. 直接给机器人发送私聊消息


---

FAQ

Q1: Scoped Prompts 是否支持自定义作用域?

目前官方支持 channelthreadim 三种内置作用域。如需扩展(如按频道名称区分),可通过 OpenClaw 的插件机制实现自定义 PromptResolver,参考 OpenClaw 文档

Q2: 升级后旧的 dm_prompt_override 配置会怎样?

配置将在启动时触发废弃警告,但会临时兼容以保证平滑过渡。建议在下次维护窗口完成迁移,预计 v0.600.0 版本将完全移除兼容层。

Q3: mrkdwn 提示对所有 LLM 都有效吗?

提示词经过针对主流模型(GPT-4、Claude 3、Llama 3)的优化测试。对于其他模型,建议在 OpenClaw 配置 中调整 mrkdwn_hints 的详细程度,或启用 format_verification 后置校验。

Q4: 如何调试提示词是否被正确加载?

启用调试日志查看作用域解析过程:

bash
OPENCLAW_LOG_LEVEL=debug openclaw run

查找包含 “prompt_resolver” 和 “scope=” 的日志行

Q5: 这些功能是否适用于 Microsoft Teams 或其他平台?

当前优化专为 Slack 的 mrkdwn 格式设计。OpenClaw 的架构支持平台适配器扩展,Teams 适配器正在开发中(追踪 Issue #58800),将提供类似的 adaptive_card_hints` 机制。

总结与下一步

本次 OpenClaw 更新通过三项关键改进,显著提升了 Slack 场景下的 AI Agent 体验:

1. Scoped Prompts 实现上下文感知的精准回复
2. mrkdwn 提示根治格式渲染问题
3. 配置简化降低维护成本

建议行动:

  • 立即升级至最新版本体验新功能
  • 审查现有 Slack Agent 配置,规划迁移时间表
  • 在测试环境中验证三种作用域的提示词效果

相关阅读

参考来源

| 来源 | 链接 |
|:—|:—|
| 本次更新 GitHub Commit | https://github.com/openclaw/openclaw/commit/a7e3c0b0e1a6f172fbeeb326369f4d9bd7a754d4 |
| OpenClaw 官方文档 | https://docs.openclaw.dev |
| Slack API 格式规范 | https://api.slack.com/reference/surfaces/formatting |
| OpenClaw 版本发布说明 | https://github.com/openclaw/openclaw/releases |