1仕組みと動作原理
わずか 240 行の単一スクリプトで構成され、その動作は「openclaw.json の読み込み → JSON の変更 → 元ファイルへの上書き」に徹しており、デーモンや API ラッパーは存在しません。
def save_config(path: Path, config: dict): with open(path, "w", encoding="utf-8") as f: json.dump(config, f, indent=2, ensure_ascii=False) f.write("\n")
設定検出は「明示的な --config によるショートカット、それ以外の場合は 3 つの候補のうち存在するものをすべて編集対象としてリストアップ」し、同時に変更すべきミラーとして扱います。
CONFIG_CANDIDATES = [ Path.home() / ".openclaw" / "openclaw.json", Path.home() / ".kimi" / "kimi-claw" / "openclaw.json", Path.home() / ".kimi_openclaw" / "openclaw.json", ]
「ミラーの同期」の実装は、プライマリ設定を他のファイルへコピーするのではなく、各設定ファイルに対して個別に同じ変更ロジックを実行する方式をとっています。
print(f"Found {len(configs)} config files; will edit and sync all:")
変更対象のファイルは書き込み前に必ず <設定ディレクトリ>/config-backups/openclaw-<ローカルタイムスタンプ>.json へバックアップされ、保持上限やプルーニングのロジックはありません。
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") backup_path = backup_dir / f"openclaw-{timestamp}.json" shutil.copy2(path, backup_path)
モデル定義が存在しない場合は組み込み定義を自動補完します。KNOWN_MODELS に k3 / k2p6 / kimi-k2.7-code の id、contextWindow、maxTokens、input モダリティがハードコードされており、provider レベルの headers を継承させます。
"k3": { "id": "k3", "name": "k3", "reasoning": True, "input": ["text", "image"], "contextWindow": 1048576, "maxTokens": 32768, },
デフォルトモデルの参照を agents.defaults.model.primary に書き込み(形式判定は行わず添字で直接代入)、同時に agents.defaults.models のエイリアスマッピングを書き換えます。
config["agents"]["defaults"]["model"]["primary"] = f"{provider_name}/{model_id}"
デフォルトモデル変更時、エイリアスマッピング内の「現在の provider/」で始まるすべてのキーを削除した上で、新モデル用の空オブジェクトのみを書き戻します。
stale_keys = [k for k in models_map if k.startswith(f"{provider_name}/")] for k in stale_keys: del models_map[k] models_map[f"{provider_name}/{model_id}"] = {}
書き換え後はメタデータのタイムスタンプを保守します。meta.lastTouchedAt に UTC ISO 時間を書き込み、meta.lastTouchedVersion が存在すれば維持、存在しなければ "unknown" を設定します。
config.setdefault("meta", {})["lastTouchedAt"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
再起動パスは `openclaw gateway restart` の 1 系統のみであり、失敗時は警告を出力してユーザーに手動再起動を促すだけで、systemd へのフォールバックはありません。
["openclaw", "gateway", "restart"],
スクリプトの枠を超えた本当の「スキルの深さ」はドキュメント層の診断プロトコルにあります。まず curl を使って <baseUrl>/v1/messages に対し実際の補完プローブを実行し、設定変更後に openclaw agent で検証を実行します。
curl -sS -o /tmp/probe.json -w "HTTP %{http_code}\n" \ -X POST "<baseUrl>/v1/messages" \ -H "Authorization: Bearer <apiKey>" \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| cli | openclaw CLI(gateway restart;脚本实际调用) |
| cli | openclaw gateway status(读 Config (service) 判断网关真正读哪份配置) |
| cli | openclaw agent --local --json(Step 4 端到端验证,看 result/fallbackUsed) |
| cli | curl(Step 2 端点探针;由 agent 按文档执行,非脚本调用) |
| cli | python3 -m json.tool(手工编辑后的 JSON 校验建议) |
| api | Anthropic Messages 兼容接口(探针目标:POST <baseUrl>/v1/messages,Bearer + anthropic-version 头) |
| network | Kimi 官方端点(references 里给出的 provider baseUrl 示例) |
| network | 本地 echo 抓包服务器(trap 目录附录的 wire-capture 配方,读真实 x-api-key) |
| package | Python 3 标准库 only(json/os/shutil/subprocess/argparse/pathlib/datetime;无第三方依赖) |
4推奨統制措置と判定根拠
- scripts/: ローカルスクリプトまたはコマンドの実行: python3 scripts/switch-model.py <model-id> [--provider NAME] [--config PATH
- 网络端点: 外部ネットワークアクセスの宣言: スクリプト自体: ネットワーク呼び出しゼロ(urllib/requests/socket/httpx なし、URL 定数なし)
- 本地工作目录: ファイル読み書きとアーティファクト出力: <検出された各 openclaw.json>(インプレース上書き、indent=2 + ensure_ascii=False + 末尾改行)
5二重目の独立検証
- [discrepancy] 文档称脚本 'syncs mirror files'(SKILL.md Step 3)是否等于内容同步 — 代码只对每个发现的文件各自施加同一处修改(循环里 load→backup→改 provider/默认模型→save),从不把主配置内容复制到其它文件;因此若两份 openclaw.json 原本就有差异,改完仍然有差异,只是「默认模型」这一处一致了。SKILL.md 的 'syncs mirror files' 与脚本实际的 'edit all of them identically' 不是一回事,属措辞夸大(脚本自己的打印也是 'will edit and sync all')。
- [discrepancy] README 声称 openclaw-model-switch 有 'Model validation against provider's model list' — 脚本里没有校验门禁:模型不在 provider 的 models 里时只做 model_exists → 若内置 KNOWN_MODELS 也没有该 id,仅打印 Warning('Model X is not defined in config and not in built-in known models.')然后照常写入并返回 0。SKILL.md 自身并未这样宣称(它靠 Step 2 探针 + Step 4 验证兜底),所以是 README 层面的描述与代码能力不符。
- [ok] 「Step 2 端点探针 / Step 4 端到端验证」是否由 skill 代码实现 — 复核确认这不是代码能力而是文档纪律:grep 全脚本无任何 HTTP 客户端与 curl 调用,Step 2/Step 4 都是给 agent 的操作指令(curl 与 openclaw agent 两条命令)。本报告已在 how_it_works 末条、capabilities 与 external_deps 中统一标注为「文档指示、非脚本执行」,避免把文档承诺记成代码能力。
- [ok] 脚本是否发网络请求 / 读 env 里的 API key(用户指定的两个重点) — 反向扫描 scripts/ 零命中:无 urllib/requests/socket/httpx/curl/wget;无 os.environ/getenv/apiKey 读取。附带发现 import os 在该文件中完全未被使用(无 os.* 调用),说明连标准库层面的环境访问都没接上——凭证交接 100% 发生在文档层(curl 头)而非代码层。
- [ok] 备份是否真的先于写入、且每份被改文件都有备份 — 循环体内顺序为 load_config → 解析 provider → backup_config(打印 '[path] backed up to: …')→ add_model_definition → update_default_model → 写 meta → save_config,备份严格前置于任何 save_config;显式 --config 时不走 discover_configs 的多文件分支,只备份那一个文件,与文档一致。
- [ok] 别名清理的破坏范围(第一遍记录「删除 stale keys」,第二遍量化影响) — 确认删除条件是 k.startswith(f"{provider_name}/"),粒度是整个 provider 前缀而非「旧模型」——同一 provider 下所有其它模型的别名映射条目都会被 del,随后只补回新模型的一条空对象(models_map[f"{provider_name}/{model_id}"] = {});若属性顺序上是先删后写,用户其它模型的显示名别名确实会永久丢失(备份可回滚)。已升级为 verdict.risks 首位。
- [ok] 多文件模式下的失败原子性 — provider 缺失时在循环体内直接 sys.exit(1),若第一个候选文件已成功写入而第二个文件里没有该 provider,进程会在中途退出:出现「有的文件改了、有的没改」的半改状态,且没有回滚逻辑(文档也未提示需人工核对全部镜像)。
- [ok] SKILL.md 的故障知识是否能在 references 里落地(声明 vs 资产) — SKILL.md 表格里的 5 类症状与 references/troubleshooting-model-config.md 的 5 条 trap 一一对应(含 env KIMI_API_KEY 劫持、插件 binary thinking、canonicalModelId 解等级、relay 分组 503、改错文件),kimi-models.md 也确实给出 k3/k2p6/kimi-k2.7-code 的规格与配置片段;文档承诺的知识资产存在且自洽,无空头引用。
6結論
c53908d204b24a25…