14 KiB
机器人卡片模型切换方案
一、背景
当前机器人总览页已经能展示每个 agent 当前使用的模型,但模型信息是只读的,用户如果想给某个 agent 切换模型,仍然需要手动修改配置。
目标是把这个操作前置到机器人卡片上,做到:
- 用户可以直接在页面上切换某个 agent 的模型
- 切换后配置被持久化保存
- 页面立即显示新模型
- 后续从 dashboard 发起的相关测试和展示都基于新模型
结合当前代码:
- 首页总览在 app/page.tsx
- 机器人卡片在 app/components/agent-card.tsx
- 配置读取在 app/api/config/route.ts
- 配置文件路径来自 lib/openclaw-paths.ts
进一步确认本机 OpenClaw 能力后,发现 OpenClaw 提供 Gateway 侧的 config.patch 能力,用于安全地做部分配置更新;并且该能力在写入后会自动触发 restart,使新配置真正生效。
因此,这个功能不能只改前端展示,也不应该由 dashboard 直接手写配置文件,而应该走 Gateway 的 config.patch 标准能力。
二、目标
本期目标:
- 在机器人卡片中增加模型切换入口
- 支持为单个 agent 选择一个新的模型
- 通过 Gateway
config.patch更新指定 agent 的模型 - 更新后自动触发 Gateway restart,使新模型真正生效
- 重启完成后刷新 dashboard 数据,使页面显示最新状态
三、非目标
本期不做以下事情:
- 不做批量切换多个 agent 模型
- 不编辑全局默认模型和 fallback 模型
- 不管理 provider 密钥或 auth profile
- 不单独设计新的 reload 协议
- 不在本期中处理 agent 不存在于
agents.list但被自动扫描出来时的持久化写回
四、用户故事
作为 dashboard 使用者,我希望直接在机器人卡片里切换某个 agent 的模型,这样我就不需要再去手动改配置,并且切换后系统会自动应用新模型。
五、现状分析
1. 模型数据来源
当前 /api/config 会读取 openclaw.json,并整理出:
agents[]providers[]defaults.modeldefaults.fallbacks
其中每个 agent 最终展示的 model 是这样来的:
- 优先使用
agent.model - 如果没有,则 fallback 到全局默认模型
2. 卡片当前能力
当前 AgentCard 只负责展示:
- agent 名称、ID、状态
- 当前模型
- 平台信息
- session 统计
它没有任何“修改模型”的动作,也没有提交回调。
3. 真正生效的配置位置
如果要让“切换模型”真的生效,最终被修改的目标配置仍然是:
config.agents.list[i].model
也就是说,配置层面上最终需要更新的是:
- 如果 agent 原来已经显式配置了
model,则直接改掉它 - 如果 agent 原来没有
model,只是继承默认模型,也要为该 agent 显式补一条model
但更新动作不由 dashboard 直接写文件,而是由 Gateway config.patch 负责执行。
六、交互方案
1. 机器人卡片中的模型区域改造
当前模型区域是:
- 模型 badge
- 模型测试状态
改造后建议为:
- 正常态:显示当前模型 badge + “切换模型”按钮
- 编辑态:显示模型选择器 + 保存 + 取消
- 保存中:按钮禁用,展示“保存并应用中”状态
- 失败态:保留编辑态,并显示错误信息
保存动作的用户心智应明确为:
- 不只是“改展示”
- 而是“更新配置并应用”
2. 推荐交互形式
采用“卡片内联编辑”,不要跳转单独页面,也不要打开重量级弹窗。
原因:
- 操作上下文最清晰,用户就是在看某个 agent 时改它
- 改动范围小,容易接入现有卡片结构
- 不需要额外复杂的页面状态管理
3. 模型选择器内容
模型选择器从首页已有的 data.providers 构造,按 provider 分组。
每个选项建议展示:
providerId / model.name- 如果没有
name,则显示providerId / model.id
最终提交值统一使用:
providerId/modelId
七、接口设计
新增一个专用写接口:
PATCH /api/config/agent-model
请求体:
{
"agentId": "main",
"model": "openai/gpt-4.1"
}
成功返回:
{
"ok": true,
"agentId": "main",
"model": "openai/gpt-4.1",
"applied": true
}
失败返回:
{
"ok": false,
"error": "Agent not found"
}
八、后端实现方案
新接口建议放在:
后端处理流程:
- 读取当前配置快照
- 校验
agentId - 校验
model - 校验目标模型是否存在于当前可用模型列表中
- 基于当前配置构造最小 patch,只修改目标 agent 的
model - 调用 Gateway
config.patch - 等待 Gateway 写入配置并自动重启
- 清除
/api/config的内存缓存 - 在前端轮询或重试拉取
/api/config/ gateway 健康状态 - 返回成功结果
推荐调用方式
后端不要直接改 openclaw.json,而是调用 OpenClaw 的 Gateway 能力:
- 先拿到当前配置快照与
baseHash - 再用
config.patch提交最小补丁
这样有几个好处:
- 只改一处,风险更低
- 与 OpenClaw 自身配置写入机制保持一致
- 写入后自动 restart,保证新模型真正生效
- 避免 dashboard 自己维护复杂的配置并发写入逻辑
patch 构造建议
后端应始终基于最新配置快照和 baseHash 构造 patch,不允许前端直接提交 patch 内容。
推荐做法:
- 先调用 Gateway
config.get - 从返回结果中提取:
- 当前配置快照
baseHash
- 在服务端内存中找到目标 agent
- 只更新该 agent 的
model - 生成最小变更 patch
- 调用 Gateway
config.patch
patch 的目标是:
- 只修改
agents.list中目标 agent 的model - 不改全局默认模型
- 不改 fallback
- 不改其他 agent 配置
- 不改 provider 配置
实现原则:
- 前端只传
agentId和model - patch 完全在后端生成
- 一切并发控制都依赖
baseHash
这样可以最大限度降低误改配置的风险。
九、校验规则
以下情况必须拒绝写入:
agentId缺失model缺失agents.list不存在- 指定 agent 不存在
- 模型不在当前已知 provider/model 列表中
- 无法获取当前配置快照或
baseHash - Gateway
config.patch调用失败
十、前端实现方案
1. 首页 app/page.tsx
需要新增的职责:
- 从
data.providers整理出modelOptions - 实现
onModelChange(agentId, model)回调 - 回调中调用
PATCH /api/config/agent-model - 成功后等待 Gateway 重启并恢复可用
- 然后复用现有
fetchData(true)刷新数据 - 把
modelOptions和onModelChange传给AgentCard
2. 卡片组件 app/components/agent-card.tsx
建议新增 props:
modelOptions?: Array<{
providerId: string
providerName: string
accessMode?: "auth" | "api_key"
models: Array<{ id: string; name: string }>
}>
onModelChange?: (agentId: string, model: string) => Promise<void>
建议新增本地状态:
isEditingModeldraftModelisSavingModelmodelSaveError
3. 卡片行为建议
进入编辑态时:
- 默认选中当前模型
- 如果当前模型不在可选列表中,也要显示一个“当前模型(未知)”占位选项,避免用户失去上下文
保存按钮启用条件:
- 已选择模型
- 新模型和当前模型不同
- 当前不在保存中
保存成功后:
- 退出编辑态
- 清空错误信息
- 页面刷新后显示最新模型
- 若有需要,可给出“模型已应用”短提示
保存失败后:
- 保持编辑态
- 保留用户当前选择
- 展示错误信息
十一、“切换后模型要生效”的定义
在新的方案下,“模型生效”定义为:
- Gateway
config.patch已成功执行 - Gateway 已按 OpenClaw 机制自动 restart
- restart 完成后
/api/config返回新模型 - 卡片显示新模型
- 后续 dashboard 内触发的模型测试、agent 测试等使用新模型
这意味着,这次不是“仅修改配置”,而是“修改配置并应用配置”。
因此它能覆盖:
- 机器人卡片展示
- 首页状态刷新
- dashboard 内测试逻辑
十二、运行时说明
本方案不再依赖“运行时是否支持热更新”的不确定性。
因为根据当前 OpenClaw 能力,config.patch 在写入后会触发 restart,所以模型切换后会通过标准 restart 流程生效。
因此,产品语义应明确为:
- dashboard 发起模型切换
- OpenClaw 通过
config.patch更新配置 - 系统自动 restart 后应用新模型
这比“假设支持热更新”更稳妥,也更符合 OpenClaw 当前已有机制。
十三、缓存处理
当前 app/api/config/route.ts 有 30 秒内存缓存。
如果不处理,模型切换后即使 Gateway 已经重启,页面也可能短时间看到旧值。
因此需要在写接口成功后清理缓存。
推荐方案:
新增共享缓存模块:
提供:
getConfigCache()setConfigCache()clearConfigCache()
然后:
GET /api/config读取/写入这个共享缓存PATCH /api/config/agent-model成功后调用clearConfigCache()
这样可以避免把缓存逻辑散落在多个文件里。
十四、安全性要求
- 不允许 dashboard 直接接受任意路径并写文件
- 只能通过 Gateway
config.patch修改配置 - 只能保存已经校验过的模型值
- 只能提交最小 patch
- 不能影响配置中的其他字段
十五、边界情况
-
agent 当前没有显式
model- 切换后为该 agent 新增显式
model
- 切换后为该 agent 新增显式
-
agent 是从文件系统自动扫描出来的,但不在
agents.list- 本期拒绝修改
- 原因是
config.patch需要稳定的配置落点
-
provider 存在,但 model 仅是推断出来的
- 只要它出现在后端返回的候选模型列表里,就允许选择
-
多个页面同时修改同一个 agent 模型
- 依赖
baseHash控制并发 - 如果底层配置已变化,则要求前端重试
- 依赖
-
Gateway 正在重启或暂时不可用
- 前端显示“正在应用配置,请稍候”
-
config.patch返回 baseHash 冲突- 提示用户刷新后重试
十六、实施步骤
- 抽取共享 config cache
- 新增
PATCH /api/config/agent-model - 在后端封装 Gateway
config.get+config.patch调用 - 在首页构造模型候选列表
- 给
AgentCard增加内联模型编辑 UI - 保存后等待 Gateway restart 完成并刷新首页数据
- 补充错误提示
- 补充必要的验证
十七、测试方案
手动测试
- 将某个 agent 从模型 A 切到模型 B,确认卡片立即更新
- 在切换过程中观察 Gateway 短暂重启,再恢复可用
- 刷新页面,确认模型 B 仍然存在
- 切换后执行“测试全部模型/测试 Agent”,确认使用的是新模型
- 给原本继承默认模型的 agent 切换模型,确认 patch 成功
- 传入非法模型,确认接口拒绝
- 模拟 Gateway 不可用或 patch 失败,确认卡片停留在编辑态并显示错误
自动化测试建议
- API 测试:合法 agent/model 可以正确触发
config.patch - API 测试:非法 model 被拒绝
- API 测试:agent 不存在时被拒绝
- API 测试:
baseHash冲突时返回可理解错误 - API 测试:成功后缓存被清除
- 组件测试:卡片可以进入编辑态并提交保存
十八、验收标准
- 每个机器人卡片都能进入模型切换模式
- 用户只能从合法模型列表中选择
- 保存后后端通过 Gateway
config.patch完成最小配置更新 - 配置更新后系统自动 restart 并应用新模型
/api/config在恢复后返回更新后的模型- 卡片无需手动刷新即可看到新模型
- dashboard 内已有测试能力仍然正常可用
- 非法输入不会破坏配置
十九、涉及文件
建议涉及这些文件:
- app/components/agent-card.tsx
- app/page.tsx
- app/api/config/agent-model/route.ts
- app/api/config/route.ts
- lib/openclaw-cli.ts
- 可选新增 lib/config-cache.ts
二十、结论
推荐采用:
- 卡片内联编辑
- 独立模型更新接口
- 后端调用 Gateway
config.patch - 利用 OpenClaw 标准 restart 流程应用配置
- 成功后清缓存并刷新首页
这是当前代码结构下最稳妥的方案,因为它复用了 OpenClaw 自带的配置更新机制,不依赖不确定的热更新行为,也能真正满足“切换后模型生效”的要求。