本文主要面向在 ArkClaw/OpenClaw 平台上自研 Skill 的开发者,介绍如何在 Skill 中实现实例标识 Header 的读取与透传,即在每次调用火山方舟(Volcengine Ark)模型网关时,将当前 ArkClaw 实例的身份标识随请求头传递给网关,从而实现实例级数据监控。
自研 Skill 接入后,模型网关可以从请求 Header 中识别请求来源,并在访问日志中记录对应的 Claw 实例信息。业务团队可基于此数据:
- 按实例、租户、版本等维度统计调用量、Token 消耗、错误率。
- 掌握自研 Skill 的整体运行状态与资源消耗情况,为容量规划和成本治理提供依据。
说明
若暂不将自研 Skill 接入 ArkClaw,不会影响 Skill 的正常运行与业务能力。但该技能产生的模型调用相关数据将无法采集统计。
已在本地开发环境中安装 Python,且版本不低于 3.10。如未安装,请前往 Python 官网 下载并安装对应版本。 较低 Python 版本存在语法、标准库兼容性问题,无法支撑 Skill 运行所需依赖,建议优先部署 3.10 及以上稳定版本。
本文适用于所有会直接或间接经过模型网关发起请求的自研 Skill,包括但不限于:
- Embedding、Rerank 等文本处理类 Skill。
- 异步任务创建、任务状态轮询、结果查询与结果下载等异步流程涉及的所有请求路径。
在 ArkClaw/OpenClaw 运行环境中,自研 Skill 通常需要调用模型网关完成模型能力调用。例如,图片生成 Skill 会调用图像生成接口,视频生成 Skill 会调用任务创建和任务轮询接口,Embedding 或 Rerank Skill 会调用文本向量化或排序接口。
在企业级 SaaS 场景中,同一套模型网关可能同时承载多个企业实例、多个租户、多个版本以及多个自研 Skill 的调用。如果请求缺少实例标识,网关只能记录接口、模型、状态码、延迟等通用信息,无法准确回答“哪个实例产生了该调用”“某个租户消耗了多少 Token”“错误率升高是否集中在某个版本或实例”等问题。
为解决上述问题,部署侧会在 OpenClaw 运行时配置中写入一组 Header。自研 Skill 在发起模型网关请求时,需要从配置中读取这组 Header,并在所有发往模型网关的 HTTP 请求中透传。模型网关从 Header 中提取实例 ID,并写入访问日志。
接入该能力的目的,是让业务团队能够从数据监控视角持续掌握 Skill 的运行状态。接入后,网关侧可按实例维度统计调用量、Token 消耗、错误率和延迟;当出现调用失败、额度异常、模型服务波动或性能下降时,也可以根据实例标识快速定位影响范围和责任实例。
在开始接入前,请确认自研 Skill 满足以下条件。
本节介绍 Header 的来源与配置结构,帮助您理解底层原理。当您购买并启动 ArkClaw 实例后,系统会自动生成该配置文件。您仅需了解相关逻辑,无需手动创建、修改此文件。
注意
- 通过模型网关发起调用的自研 Skill,务必读取并透传实例标识 Header。一旦缺少该 Header,网关无法识别请求所属 ArkClaw 实例,会影响调用统计、用量分析、故障排查与租户问题定位。
- 所有 Header 名称与内容均由部署侧统一配置下发,自研 Skill 仅负责完整读取并透传 Header。禁止硬编码 Header 名称,同时不要依赖、解析 Header 值的内部格式。
模型调用所需自定义 Header 存放于 OpenClaw 运行时配置文件,路径如下:
/root/.openclaw/openclaw.json
实例标识 Header 位于以下 JSON 路径:
models.providers.<provider_name>.models[0].headers
说明
其中<provider_name>为部署侧配置的provider名称,Skill 无需感知该参数具体取值。
示例配置如下:
说明
- X-Client-Request-Id 的数值携带版本信息与实例 ID。
- 当前示例中的X-Client-Request-Id只是部署侧配置结果,不是 Skill 代码中的固定常量。后续如果部署侧调整 Header 名称、增加多个 Header,或者修改实例标识编码规则,Skill 的透传逻辑不应受到影响。
"baseUrl": "https://ark.cn-beijing.volces.com/api/v3",
"apiKey": "8xii65gr0h6rv********8",
"id": "doubao-seedream-5-0-260128",
"X-Client-Request-Id": "enterprise-arkclaw/0507/i-yesb5ge41s4c5q****"
Header 透传是数据监控、统计和排障能力的一部分,不应影响自研 Skill 的核心业务功能。配置文件不存在、JSON 损坏、字段缺失、字段类型不符合预期、Header 内容为空等情况,都应降级为空 Header,而不是让 Skill 启动失败或请求失败。
该原则适用于个人开发环境、单元测试环境、临时调试环境和部分未接入 OpenClaw 配置的运行环境。在这些环境中,没有实例标识并不一定代表业务不可用,因此代码必须优雅降级。
配置中的 Header 由部署侧统一写入,但 Skill 自身仍应掌控认证和内容类型。合并 Header 时,必须过滤配置中可能出现的authorization和content-type,并由 Skill 自己设置Authorization和Content-Type。
从而,可以避免错误配置覆盖 Skill 的 API Key、Bearer Token 或请求体类型,降低因 Header 冲突导致认证失败、请求格式错误或安全边界混淆的风险。
只要请求发往模型网关,就应使用同一套 Header 构造逻辑。透传范围不应只覆盖创建任务的 POST 请求,也应覆盖轮询任务状态的 GET 请求、下载结果的请求、查询任务详情的请求、取消任务的请求以及其他模型网关相关请求。
在异步任务场景中,创建任务和轮询任务通常由不同函数、不同模块甚至不同调用路径实现。开发者需要确保这些调用路径都复用统一的 Header 构造函数,避免出现主请求可归因、轮询请求不可归因的问题。
Header 名称、Header 值以及实例标识格式均由部署侧配置决定。Skill 不应在代码中硬编码X-Client-Request-Id,也不应通过字符串切分、正则匹配或固定路径解析实例 ID。
Skill 的职责是读取 models.providers.*.models[0].headers 中的有效 Header,并原样透传到模型网关。这样可以保证部署侧在调整 Header 策略时,不需要同步修改每个 Skill 的业务代码。
建议将 Header 合并逻辑收口在 HTTP 客户端或网关调用模块中,而不是分散在各个业务函数中手写。统一收口可以降低遗漏概率,也便于后续增加日志、测试、灰度开关或额外安全过滤逻辑。
对于已有 Skill,如果当前代码在多个函数中分别构造 Header,建议逐步改造为公共函数,例如_build_headers(auth_config),并要求所有模型网关请求都调用该函数。
Header 透传逻辑应尽量与业务参数、模型参数和响应解析解耦。读取配置、提取 Header、挂载到认证配置、请求时合并 Header,应形成清晰的分层,不应把网关标识逻辑散落到 payload 构造、任务状态解析或结果下载逻辑中。
自研 Skill 接入 ArkClaw 实例标识透传能力,建议按照“读取配置 > 提取 Header > 挂载认证配置 > 请求时合并 Header”的四步顺序实现。
读取并解析 OpenClaw 的配置文件/root/.openclaw/openclaw.json。
说明
- 配置文件在开发环境、单元测试、个人部署场景下可能不存在,同时存在 JSON 损坏、权限异常、字段缺失等风险。所有异常需降级返回空配置,避免 Skill 崩溃。
- 该函数职责仅限读取、解析配置,不处理认证、网络请求等业务逻辑,职责单一,便于各类异常场景单测覆盖。
from pathlib import Path
DEFAULT_OPENCLAW_CONFIG_FILE = "/root/.openclaw/openclaw.json"
def _read_openclaw_config(
config_file: str = DEFAULT_OPENCLAW_CONFIG_FILE,
path = Path(config_file)
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return data if isinstance(data, dict) else {}
步骤二:提取首个可用的 Provider Headers
获取配置字典后,遍历models.providers下所有provider,尝试提取服务商内首个模型对应的headers字段,并增加多层健壮性校验:
- 若models 、providers节点非字典类型,直接终止查找。
- 对Header的键、值进行字符串类型校验,去除首尾空白字符,并过滤空内容条目。
- 一旦解析得到有效Header集合,立即返回,不再继续遍历。
def _first_provider_request_headers(
openclaw_config: dict[str, Any],
models_section = openclaw_config.get("models")
if not isinstance(models_section, dict):
providers_section = models_section.get("providers")
if not isinstance(providers_section, dict):
for provider in providers_section.values():
if not isinstance(provider, dict):
models = provider.get("models")
if not isinstance(models, list) or not models:
if not isinstance(first_model, dict):
raw_headers = first_model.get("headers")
if not isinstance(raw_headers, dict):
key.strip(): value.strip()
for key, value in raw_headers.items()
and isinstance(value, str)
将提取到的 Header 存入 Skill 的认证或运行时配置对象,例如AuthConfig,供 HTTP 层统一使用。
说明
所有 HTTP 请求层统一从 Skill 的认证/运行时配置对象获取 Header,不直接读取 OpenClaw 配置文件。
from dataclasses import dataclass, replace
request_headers: dict[str, str] | None = None
def load_auth_config(...) -> AuthConfig:
config = _load_base_config(...)
request_headers = _first_provider_request_headers(
_read_openclaw_config(config_file)
return replace(config, request_headers=request_headers or None)
所有发往火山方舟模型网关的HTTP请求,均需要统一合并请求头(Header),遵循如下规则:
- 自动过滤配置内的authorization、content-type (不区分大小写),避免配置内容意外覆盖 Skill 自身鉴权信息与报文类型。
- 追加 Skill 内置的Authorization、Content-Type,保障 Skill 自身凭证优先级最高。
def _build_headers(auth_config: AuthConfig) -> dict[str, str]:
for key, value in (auth_config.request_headers or {}).items()
if key.lower() not in {"authorization", "content-type"}
"Content-Type": "application/json",
"Authorization": f"Bearer {auth_config.api_key}",
所有访问模型网关的请求(创建任务 POST、轮询结果 GET 等)统一复用这套Header构建逻辑,不可仅在主请求附加 Header、遗漏轮询等辅助请求。若 Skill 同时存在同步、异步 HTTP 客户端,需保证Header构造规则完全一致,避免部分调用缺失实例标识Header,造成网关监控日志残缺。
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.post(
f"{auth_config.api_base}/images/generations",
headers=_build_headers(auth_config),
新增对接模型网关的自研 Skill,上线前请逐项完成以下核验: